wpForo Forum - Version 2.0.1

Version Description

Download this release

Release Info

Developer Tomdever
Plugin Icon 128x128 wpForo Forum
Version 2.0.1
Comparing to
See all releases

Code changes from version 1.9.9.2 to 2.0.1

Files changed (191) hide show
  1. admin/assets/css/admin.css +878 -0
  2. {wpf-admin → admin/assets}/css/deactivation-dialog.css +0 -0
  3. {wpf-admin → admin/assets}/css/index.html +0 -0
  4. {wpf-admin → admin/assets}/html/index.html +0 -0
  5. {wpf-admin → admin/assets}/html/privacy-policy-gdpr.html +0 -0
  6. {wpf-admin → admin/assets}/html/simple-forum-rules.html +0 -0
  7. admin/assets/js/backend.js +498 -0
  8. {wpf-admin/includes → admin/assets/js/contenthover}/index.html +0 -0
  9. {wpf-admin → admin/assets}/js/contenthover/jquery.contenthover.min.js +0 -0
  10. {wpf-admin → admin/assets}/js/deactivation-dialog.js +3 -2
  11. {wpf-admin → admin/assets/js}/index.html +0 -0
  12. admin/assets/third-party/iconpicker/fontawesome-iconpicker.js +1038 -0
  13. admin/assets/third-party/iconpicker/fontawesome-iconpicker.min.css +9 -0
  14. {wpf-admin → admin/assets}/xml/english.xml +1 -1
  15. {wpf-admin/js/contenthover → admin/assets/xml}/index.html +0 -0
  16. {wpf-admin → admin/includes}/deactivation-dialog.php +45 -45
  17. {wpf-admin/js → admin/includes}/index.html +0 -0
  18. admin/index.php +246 -0
  19. admin/listtables/Boards.php +168 -0
  20. admin/listtables/Members.php +440 -0
  21. wpf-admin/includes/moderation-listtable.php → admin/listtables/Moderations.php +169 -129
  22. wpf-admin/includes/phrase-listtable.php → admin/listtables/Phrases.php +123 -109
  23. {wpf-admin/options-tabs → admin/listtables}/index.html +0 -0
  24. admin/pages/accesses.php +139 -0
  25. admin/pages/addons.php +84 -0
  26. admin/pages/board.php +213 -0
  27. admin/pages/dashboard.php +132 -0
  28. admin/pages/forum.php +491 -0
  29. {wpf-admin/tools-tabs → admin/pages}/index.html +0 -0
  30. admin/pages/member.php +83 -0
  31. admin/pages/moderation.php +61 -0
  32. admin/pages/overview.php +160 -0
  33. admin/pages/phrase.php +189 -0
  34. admin/pages/settings.php +199 -0
  35. admin/pages/themes.php +101 -0
  36. admin/pages/tools.php +32 -0
  37. admin/pages/usergroup.php +423 -0
  38. admin/settings/akismet.php +19 -0
  39. admin/settings/antispam.php +102 -0
  40. admin/settings/authorization.php +31 -0
  41. admin/settings/board.php +38 -0
  42. admin/settings/buddypress.php +9 -0
  43. admin/settings/components.php +18 -0
  44. admin/settings/email.php +51 -0
  45. admin/settings/forums.php +43 -0
  46. admin/settings/general.php +47 -0
  47. admin/settings/legal.php +41 -0
  48. admin/settings/logging.php +14 -0
  49. admin/settings/members.php +10 -0
  50. admin/settings/notifications.php +12 -0
  51. admin/settings/posting.php +75 -0
  52. admin/settings/profiles.php +21 -0
  53. admin/settings/rating.php +34 -0
  54. admin/settings/recaptcha.php +16 -0
  55. admin/settings/rss.php +10 -0
  56. admin/settings/seo.php +14 -0
  57. admin/settings/social.php +130 -0
  58. admin/settings/styles.php +137 -0
  59. admin/settings/subscriptions.php +56 -0
  60. admin/settings/tags.php +11 -0
  61. admin/settings/topics.php +89 -0
  62. admin/settings/um.php +8 -0
  63. admin/tools-tabs/cleanup.php +55 -0
  64. admin/tools-tabs/debug.php +343 -0
  65. {wpf-admin/xml → admin/tools-tabs}/index.html +0 -0
  66. admin/tools-tabs/misc.php +116 -0
  67. admin/tools-tabs/tables.php +178 -0
  68. {wpf-assets → assets}/addons/ad-manager/header-off.png +0 -0
  69. {wpf-assets → assets}/addons/ad-manager/header.png +0 -0
  70. {wpf-assets → assets}/addons/ad-manager/index.html +0 -0
  71. {wpf-assets → assets}/addons/attachments/header-off.png +0 -0
  72. {wpf-assets → assets}/addons/attachments/header.png +0 -0
  73. {wpf-assets → assets}/addons/attachments/index.html +0 -0
  74. {wpf-assets → assets}/addons/cross/header-off.png +0 -0
  75. {wpf-assets → assets}/addons/cross/header.png +0 -0
  76. {wpf-assets → assets}/addons/cross/index.html +0 -0
  77. {wpf-assets → assets}/addons/embeds/header-off.png +0 -0
  78. {wpf-assets → assets}/addons/embeds/header.png +0 -0
  79. {wpf-assets → assets}/addons/embeds/index.html +0 -0
  80. {wpf-assets → assets}/addons/giphy/header-off.png +0 -0
  81. {wpf-assets → assets}/addons/giphy/header.png +0 -0
  82. {wpf-assets → assets}/addons/index.html +0 -0
  83. {wpf-assets → assets}/addons/mycred/header-off.png +0 -0
  84. {wpf-assets → assets}/addons/mycred/header.png +0 -0
  85. {wpf-assets → assets}/addons/mycred/index.html +0 -0
  86. {wpf-assets → assets}/addons/pm/header-off.png +0 -0
  87. {wpf-assets → assets}/addons/pm/header.png +0 -0
  88. {wpf-assets → assets}/addons/pm/index.html +0 -0
  89. {wpf-assets → assets}/addons/polls/header-off.png +0 -0
  90. {wpf-assets → assets}/addons/polls/header.png +0 -0
  91. {wpf-assets → assets}/addons/polls/index.html +0 -0
  92. {wpf-assets → assets}/addons/prefix/header-off.png +0 -0
  93. {wpf-assets → assets}/addons/prefix/header.png +0 -0
  94. {wpf-assets → assets}/addons/syntax/header-off.png +0 -0
  95. {wpf-assets → assets}/addons/syntax/header.png +0 -0
  96. {wpf-assets → assets}/addons/tcf/header-off.png +0 -0
  97. {wpf-assets → assets}/addons/tcf/header.png +0 -0
  98. {wpf-assets → assets}/addons/tenor/header-off.png +0 -0
  99. {wpf-assets → assets}/addons/tenor/header.png +0 -0
  100. {wpf-assets → assets}/addons/ucf/header-off.png +0 -0
  101. {wpf-assets → assets}/addons/ucf/header.png +0 -0
  102. {wpf-assets → assets}/addons/ucf/index.html +0 -0
  103. {wpf-assets → assets}/addons/wpforo-emoticons/header-off.png +0 -0
  104. {wpf-assets → assets}/addons/wpforo-emoticons/header.png +0 -0
  105. {wpf-assets → assets}/addons/wpforo-emoticons/index.html +0 -0
  106. {wpf-assets → assets}/css/font-awesome/LICENSE.txt +0 -0
  107. assets/css/font-awesome/css/fontawesome-all.min.css +6 -0
  108. {wpf-assets/css/font-awesome/css → assets/css/font-awesome}/index.html +0 -0
  109. assets/css/font-awesome/webfonts/fa-brands-400.ttf +0 -0
  110. assets/css/font-awesome/webfonts/fa-brands-400.woff2 +0 -0
  111. assets/css/font-awesome/webfonts/fa-regular-400.ttf +0 -0
  112. assets/css/font-awesome/webfonts/fa-regular-400.woff2 +0 -0
  113. assets/css/font-awesome/webfonts/fa-solid-900.ttf +0 -0
  114. assets/css/font-awesome/webfonts/fa-solid-900.woff2 +0 -0
  115. assets/css/font-awesome/webfonts/fa-v4compatibility.ttf +0 -0
  116. assets/css/font-awesome/webfonts/fa-v4compatibility.woff2 +0 -0
  117. {wpf-assets/css/font-awesome → assets/css}/index.html +0 -0
  118. {wpf-assets → assets}/css/wpforo-sitemap.xsl +0 -0
  119. {wpf-assets → assets}/images/ajax_loading.gif +0 -0
  120. assets/images/category-bg.jpg +0 -0
  121. assets/images/dashboard/addon.png +0 -0
  122. assets/images/dashboard/akismet.png +0 -0
  123. assets/images/dashboard/arrow-right.png +0 -0
  124. assets/images/dashboard/cog.png +0 -0
  125. assets/images/dashboard/dots.png +0 -0
  126. {wpf-assets/css/font-awesome/webfonts → assets/images/dashboard}/index.html +0 -0
  127. assets/images/dashboard/new.png +0 -0
  128. assets/images/dashboard/note.png +0 -0
  129. assets/images/dashboard/ok.png +0 -0
  130. assets/images/dashboard/wpforo-logo.png +0 -0
  131. assets/images/dashboard/wpforo.png +0 -0
  132. {wpf-assets/css → assets/images}/index.html +0 -0
  133. {wpf-assets → assets}/images/loading.gif +0 -0
  134. {wpf-assets → assets}/images/noavatar.png +0 -0
  135. assets/images/profile-bg.jpg +0 -0
  136. {wpf-assets → assets}/images/sn/aim.png +0 -0
  137. {wpf-assets → assets}/images/sn/facebook.png +0 -0
  138. {wpf-assets → assets}/images/sn/fb-m.jpg +0 -0
  139. {wpf-assets → assets}/images/sn/fb-m.png +0 -0
  140. {wpf-assets → assets}/images/sn/gg-m.png +0 -0
  141. {wpf-assets → assets}/images/sn/gtalk.png +0 -0
  142. {wpf-assets → assets}/images/sn/icq.png +0 -0
  143. {wpf-assets/images → assets/images/sn}/index.html +0 -0
  144. {wpf-assets → assets}/images/sn/lin-m.png +0 -0
  145. {wpf-assets → assets}/images/sn/linkedin.png +0 -0
  146. {wpf-assets → assets}/images/sn/location-1.png +0 -0
  147. {wpf-assets → assets}/images/sn/location-2.png +0 -0
  148. {wpf-assets → assets}/images/sn/location-3.png +0 -0
  149. {wpf-assets → assets}/images/sn/location-4.png +0 -0
  150. {wpf-assets → assets}/images/sn/location-5.png +0 -0
  151. {wpf-assets → assets}/images/sn/location-6.png +0 -0
  152. {wpf-assets → assets}/images/sn/msn.png +0 -0
  153. {wpf-assets → assets}/images/sn/ok-m.png +0 -0
  154. {wpf-assets → assets}/images/sn/share-m.png +0 -0
  155. {wpf-assets → assets}/images/sn/skype.png +0 -0
  156. {wpf-assets → assets}/images/sn/toggle-1.png +0 -0
  157. {wpf-assets → assets}/images/sn/toggle-2.png +0 -0
  158. {wpf-assets → assets}/images/sn/toggle-3.png +0 -0
  159. {wpf-assets → assets}/images/sn/toggle-4.png +0 -0
  160. {wpf-assets → assets}/images/sn/tw-m.png +0 -0
  161. {wpf-assets → assets}/images/sn/twitter.png +0 -0
  162. {wpf-assets → assets}/images/sn/vk-m.png +0 -0
  163. {wpf-assets → assets}/images/sn/wapp-m.png +0 -0
  164. {wpf-assets → assets}/images/sn/whatsapp.png +0 -0
  165. {wpf-assets → assets}/images/sn/yahoo.png +0 -0
  166. {wpf-assets → assets}/images/wpforo-40.png +0 -0
  167. {wpf-assets → assets}/images/wpforo-info.png +0 -0
  168. {wpf-assets → assets}/images/wpforo-logo.png +0 -0
  169. {wpf-assets/images/sn → assets}/index.html +0 -0
  170. {wpf-assets → assets}/js/ajax.js +538 -43
  171. assets/js/bootstrap-popover.min.js +9 -0
  172. {wpf-assets → assets}/js/frontend.js +92 -66
  173. {wpf-assets → assets/js}/index.html +0 -0
  174. {wpf-assets → assets}/js/snfb.js +0 -0
  175. {wpf-assets → assets}/js/tinymce-code.js +0 -0
  176. {wpf-assets → assets}/js/tinymce-emoji.js +0 -0
  177. {wpf-assets → assets}/js/tinymce-link.js +0 -0
  178. {wpf-assets → assets}/js/tinymce-pre.js +0 -0
  179. {wpf-assets → assets}/js/tinymce-spoiler.js +0 -0
  180. {wpf-assets → assets}/js/widgets.js +0 -0
  181. autoload.php +34 -0
  182. classes/API.php +961 -0
  183. classes/Actions.php +2835 -0
  184. classes/Activity.php +683 -0
  185. classes/Boards.php +489 -0
  186. classes/Cache.php +457 -0
  187. classes/Feed.php +148 -0
  188. classes/Forms.php +1522 -0
  189. classes/Forums.php +1260 -0
  190. classes/Logs.php +403 -0
  191. classes/Members.php +154 -0
admin/assets/css/admin.css ADDED
@@ -0,0 +1,878 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #wpf-admin-wrap .menu_edit{color: #a7a7a7;cursor: pointer;}
2
+ #wpf-admin-wrap .menu_edit:hover{color: #004eb7;cursor: pointer;}
3
+ #wpf-admin-wrap .menu_delete{color: #a7a7a7;cursor: pointer;}
4
+ #wpf-admin-wrap .menu_delete:hover{color: #eb3034;cursor: pointer;}
5
+ #wpf-admin-wrap .forum_width{width: 65%;}
6
+ #wpf-admin-wrap .forumtitle{font-size:14px;font-weight:bold;width: 52%;}
7
+ #wpf-admin-wrap #forum-hierarchy .menu-item-bar .menu-item-handle{ max-width: none; }
8
+ #wpf-admin-wrap .item-controls{font-size:13px;}
9
+ #wpf-admin-wrap .item-controls span a img{ vertical-align:middle;}
10
+ #wpf-admin-wrap .root_forum{text-decoration: none!important;color:#231f1f; }
11
+ #wpf-admin-wrap .forum_submit {margin: 0 5px 10px 5px; min-width: 80px !important;}
12
+ #wpf-admin-wrap .wpf-delete.button, #wpf-admin-wrap .preview.button{display: inline-block !important;margin-bottom: 10px !important;}
13
+ #wpf-admin-wrap .item-controls{top: 10%!important;}
14
+ #forum_submit{width: 80px!important;}
15
+ #usergroup_table{width:98%;}
16
+ #use_us_cat{width: 1%!important;}
17
+ #wpf-admin-wrap .form-field textarea{width: 100% !important;}
18
+ #wpf-admin-wrap .option_box{ margin: 30px;padding: 30px; width: 30%;height: 20%;background: #e9e9eb;border: 1px dotted black;border-radius: 10px;float: left;overflow: auto;}
19
+ #wpf-admin-wrap .option_box label{margin: 0.5%;font-weight: bold;}
20
+ #wpf-admin-wrap .wpfadmin_notice{cursor: pointer;}
21
+ #wpf-admin-wrap .wp-color-result:after{white-space:nowrap;}
22
+ #wpf-admin-wrap .wpf-table-box-left{ width:30%; margin:0; padding:10px; border:#aaa 1px dashed; float:left; background:#FDFDFD; }
23
+ #wpf-admin-wrap .wpf-td-label{ font-weight:bold; font-size:13px; line-height:16px; }
24
+ #wpf-admin-wrap .wpf-td-label-left{ font-weight:bold; font-size:13px; line-height:16px; text-align:left; vertical-align:top; }
25
+ #wpf-admin-wrap .wpf-td-label-left-p{ font-weight:bold; font-size:13px; line-height:16px; text-align:left; vertical-align:top; padding:5px 1px; }
26
+ #wpf-admin-wrap .wpf-dw-td{ vertical-align:top; text-align:left; width:40%; font-weight:bold;line-height: 2.1em; }
27
+ #wpf-admin-wrap .wpf-dw-td-nowrap{width: 90%; vertical-align:top; text-align:left; font-weight:bold; white-space:nowrap; line-height: 2.1em; }
28
+ #wpf-admin-wrap .wpf-dw-td-value{ vertical-align:top; text-align:left;line-height: 2.1em; }
29
+ #wpf-admin-wrap .wpf-dw-td-g{ width:50%; text-align:right; vertical-align:top; padding-right:10px; font-weight:bold; }
30
+ #wpf-admin-wrap .wpf-dw-td-value-b{ vertical-align:bottom; text-align:left; }
31
+ #wpf-admin-wrap .wpf-dw-td-value-p{ vertical-align:top; text-align:left; padding:5px 1px; }
32
+ #wpf-admin-wrap label i.fa-question-circle{ padding: 0 3px;}
33
+ #wpf-admin-wrap input[type="color"]{display: inline-block; vertical-align: middle; width: 100px; padding: 4px; height: 27px;}
34
+ #wpf-admin-wrap #wpf_usergroup_colorx{display: inline-block; vertical-align: middle; width: 100px; border-color: green; min-height: 27px; line-height: 20px; padding: 0 7px;}
35
+
36
+ /****************************************************/
37
+ /************* Admin members bar ********************/
38
+ /****************************************************/
39
+
40
+ .author-rating {
41
+ height: 8px;
42
+ border: 1px solid #E0E0E0;
43
+ background: #F5F5F5;
44
+ width: 90px;
45
+ margin: 15px auto 2px 0;
46
+ padding: 0;
47
+ }
48
+ .author-rating .bar{ height:8px; }
49
+
50
+ .wpfw-0 { width:0; }
51
+ .wpfw-10{ width:10%; }
52
+ .wpfw-20{ width:20%; }
53
+ .wpfw-30{ width:30%; }
54
+ .wpfw-40{ width:40%; }
55
+ .wpfw-50{ width:50%; }
56
+ .wpfw-60{ width:60%; }
57
+ .wpfw-70{ width:70%; }
58
+ .wpfw-80{ width:80%; }
59
+ .wpfw-90{ width:90%; }
60
+ .wpfw-100{ width:100%; }
61
+
62
+ .wpfcl-a{ color:#68A3C1!important; } /* Blue Dark */
63
+ .wpfbg-a{ background-color:#68A3C1!important; }
64
+ .wpfcl-b{ color:#43A6DF!important; } /* Blue Dark */
65
+ .wpfbg-b{ background-color:#43A6DF!important; }
66
+ .wpfcl-0{ color:#999999!important; } /* Grey */
67
+ .wpfbg-0{ background-color:#999999!important; }
68
+ .wpfcl-1{ color:#555555!important; } /* Grey - Black */
69
+ .wpfbg-1{ background-color:#555555!important; }
70
+ .wpfcl-2{ color:#777777!important; } /* Grey */
71
+ .wpfbg-2{ background-color:#777777!important; }
72
+ .wpfcl-3{ color:#ffffff!important; } /* White */
73
+ .wpfbg-3{ background-color:#ffffff!important; }
74
+ .wpfcl-4{ color:#72CCFC!important;} /* Blue Light */
75
+ .wpfbg-4{ background-color:#72CCFC!important; }
76
+ .wpfcl-5{ color:#FF812D!important; } /* Orange */
77
+ .wpfbg-5{ background-color:#FF812D!important; }
78
+ .wpfcl-6{ color:#0099CC!important; } /* Blue */
79
+ .wpfbg-6{ background-color:#0099CC!important; }
80
+ .wpfcl-7{ color:#E6E6E6!important; } /* Grey */
81
+ .wpfbg-7{ background-color:#E6E6E6!important; }
82
+ .wpfcl-8{ color:#4DCA5C!important; } /* Green */
83
+ .wpfbg-8{ background-color:#4DCA5C!important; }
84
+ .wpfcl-9{ color:#f5f5f5!important; } /* Grey */
85
+ .wpfbg-9{ background-color:#f5f5f5!important; }
86
+
87
+
88
+
89
+ /**************************/
90
+ /* Global *****************/
91
+ /**************************/
92
+
93
+ #wpf-admin-wrap{ margin-top:0; }
94
+ #wpf-admin-wrap .wpf-delete{ color:#aa0000;}
95
+ #wpf-admin-wrap .wpf-delete:hover{ color:#ff0000;}
96
+ .wpforo-themes .wpf-delete{ color:#aa0000;}
97
+ .wpforo-themes .wpf-delete:hover{ color:#ff0000;}
98
+ #wpf-admin-wrap .show-settings { border:none; text-decoration:none; overflow:hidden; outline:none; }
99
+ #wpf-admin-wrap .wpforo-search-box{ display:block; margin:15px 0 5px 0; text-align:right; }
100
+ #wpf-admin-wrap .wpf-red{ color:#D74245; }
101
+ #wpf-admin-wrap .wpf-green{ color:#42B720; }
102
+
103
+ /**************************/
104
+ /* Forums *****************/
105
+ /**************************/
106
+ #wpf-admin-wrap #forum-hierarchy .dashicons{ vertical-align: middle; transition: none; font-size: 18px; }
107
+ #wpf-admin-wrap #forum-hierarchy .item-controls{ border-left: 1px dotted; padding-left: 22px; display: flex; justify-content: center; align-items: center;}
108
+ #wpf-admin-wrap #forum-hierarchy .item-controls a{ opacity: 0.8; }
109
+ #wpf-admin-wrap #forum-hierarchy .item-controls a:hover{ opacity: 1; }
110
+ #wpf-admin-wrap .menu-item-bar > .forum_width{ background: #F6F6F6; border-left: 1px dotted #666666; border-right: 1px dotted #666666}
111
+ #wpf-admin-wrap .menu-item-depth-0 > .menu-item-bar > .forum_width .item-controls{ border-color: #FFFFFF;}
112
+ #wpf-admin-wrap .menu-item-bar > .forum_width .item-controls{ border-color: #666666; font-size:16px;}
113
+ #wpf-admin-wrap .menu-item-depth-0 > .menu-item-bar > .forum_width{ background: #43A6DF; color: white}
114
+ #wpf-admin-wrap .menu-item-bar > .forum_width:hover{ border: 1px solid #666666}
115
+ #wpf-admin-wrap .menu-item-depth-0 > .menu-item-bar > .forum_width a{ color: white; font-size:16px; padding:0 2px; border-radius:10%;}
116
+ #wpf-admin-wrap .menu-item-bar > .forum_width .wpforo-cat-layout { font-size:12px;}
117
+ #wpf-admin-wrap .inside .main .wpforo-dashboard-logo{ width:140px; height:auto;}
118
+ #wpf-admin-wrap .vert_tab{ display: block; margin-top: 1px;}
119
+ #wpf-admin-wrap .nav-tab-wrapper.vert_tab{ display: inline-block; width: 17%; border: none; vertical-align: top; margin: 0 !important; padding: 0 !important; float: left; position:relative; z-index:9;}
120
+ #wpf-admin-wrap .nav-tab.vert_tab{ border-bottom: 1px solid #ccc}
121
+ #wpf-admin-wrap .nav-tab-wrapper.vert_tab > a:first-child{ margin-top: 0}
122
+ #wpf-admin-wrap .nav-tab-active.vert_tab{ border-bottom-color: #ccc !important; border-right-color:#fff;}
123
+ #wpf-admin-wrap .plugins-tab-wrap{ width: 83%; min-height: 700px; border: 1px solid #cccccc; display: inline-block; margin-left: -1px; padding: 5px 25px; box-sizing: border-box; float: left}
124
+ #wpf-admin-wrap .nav-tab-wrapper > a.nav-tab:last-child{ float: right}
125
+ #wpf-admin-wrap .nav-tab-wrapper > a.nav-tab.vert_tab{ float: none !important}
126
+ #wpf-admin-wrap .nav-tab-wrapper > a.nav-tab.vert_tab:last-child{ float: none !important}
127
+
128
+
129
+ /**************************/
130
+ /* SETTINGS ***************/
131
+ /**************************/
132
+
133
+ #wpf-admin-wrap .nav-tab{ padding: 7px 12px; }
134
+ #wpf-admin-wrap .nav-tab-active,
135
+ #wpf-admin-wrap .nav-tab-active:hover{ background-color:#FFFFFF; border-bottom-color:#FFFFFF; }
136
+ #wpf-admin-wrap .wpf-info-bar { background: none repeat scroll 0 0 #fff; border: 1px solid #e5e5e5; box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05); font-size: 13px; line-height: 2.1em; margin:0; overflow: auto; padding: 10px 15px 15px 15px; position: relative; border-top:none; }
137
+ #wpf-admin-wrap .major-publishing-actions{ margin-top:20px; }
138
+ #wpf-admin-wrap .wpf-label-big{ font-size:16px; display:table; margin-bottom:13px; margin-top:10px; }
139
+ #wpf-admin-wrap .wpf-field-small{ width:25% }
140
+ #wpf-admin-wrap .wpf-field-medium{ width:50% }
141
+ #wpf-admin-wrap .wpf-field-full{ width:100% }
142
+ #wpf-admin-wrap .wpf-info, #wpf_deactivation_dialog_wrap .wpf-info{ color:#777; font-size:13px; font-style:italic; font-weight:400; line-height:18px; margin:0;}
143
+
144
+ .wpforo_settings_table{ width:100%; padding:0; margin:2px; border-spacing: 0; border-collapse: collapse;}
145
+ .wpforo_settings_table th, .wpforo_settings_table td { padding:8px 10px; text-align:left; vertical-align:top; }
146
+ .wpforo_settings_table th{ font-weight:bold; font-size:14px; width:42%; }
147
+ /*.wpforo_settings_table tr:nth-child(odd) { background: #f5f5f5; }
148
+ .wpforo_settings_table tr:nth-child(even) { background: #FFFFFF; }*/
149
+ .wpforo_settings_table input[type="text"],
150
+ .wpforo_settings_table input[type="password"],
151
+ .wpforo_settings_table input[type="email"],
152
+ .wpforo_settings_table input[type="url"],
153
+ .wpforo_settings_table textarea{ padding:5px 10px; width:70%; }
154
+ .wpforo_settings_table select{ min-width:150px;}
155
+ .wpforo_settings_table input[type="number"]{ padding:1px 2px 1px 10px; width:30%; }
156
+ .wpforo_settings_foot{ clear:both; width:100%; text-align:right; padding:15px 0 5px 0; margin:0 0 0 0; border-top:#f8f8f8 solid 3px; }
157
+ .wpforo_settings_foot input:first-child{ float: right }
158
+ .wpforo_settings_foot input:last-child{ float: left }
159
+ .wpforo_settings_foot::after{
160
+ content: "";
161
+ clear: both;
162
+ display: table;
163
+ }
164
+
165
+ .wpf-switch-field input { display: none;}
166
+ .wpf-switch-field label {float: left;}
167
+ .wpf-switch-field label {display: inline-block; line-height: 20px; min-width:80px; background-color: #e4e4e4;color:#333;font-size: 13px;font-weight: normal;text-align: center;text-shadow: none;padding: 2px 6px;border: 1px solid rgba(0, 0, 0, 0.2);-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, 255, 0.1);box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, 255, 0.1);-webkit-transition: all 0.1s ease-in-out;-moz-transition: all 0.1s ease-in-out; -ms-transition: all 0.1s ease-in-out; -o-transition: all 0.1s ease-in-out;transition: all 0.1s ease-in-out;}
168
+ .wpf-switch-field label:hover {cursor: pointer;}
169
+ .wpf-switch-field input:checked + label {background-color: #43A6DF; -webkit-box-shadow: none; box-shadow: none; color:#fff;} /* #66DD8D */
170
+ /* .wpf-switch-field input:last-of-type:checked + label {background-color:#F77059; -webkit-box-shadow: none; box-shadow: none;} */
171
+ .wpf-switch-field input[disabled] + label {opacity: 0.5;} /* #66DD8D */
172
+ .wpf-switch-field label:first-of-type {border-radius: 4px 0 0 4px;}
173
+ .wpf-switch-field label:last-of-type {border-radius: 0 4px 4px 0;}
174
+
175
+ /**************************/
176
+ /* SETTINGS > GENERAL *****/
177
+ /**************************/
178
+
179
+
180
+ /**************************/
181
+ /* SETTINGS > MEMBERS *****/
182
+ /**************************/
183
+
184
+ #wpf-rating-table{ width:100%; font-size:12px; }
185
+ #wpf-rating-table input{ font-size:12px; height: 24px; width:100%; }
186
+ #wpf-rating-table input[type="checkbox"]{ width: 16px; height: 16px;}
187
+ #wpf-rating-table i{}
188
+ #wpf-rating-table h4{ margin:0; padding:0; font-size:14px; font-weight:bold; white-space:nowrap; }
189
+ #wpf-rating-table .wpf-badge-short{ color:#FFFFFF; white-space:nowrap; font-size:15px; line-height:22px; font-weight:bold; text-align:center; display:inline-block; padding:2px 10px; min-width:30px;}
190
+ #wpf-rating-table .wpf-badge-full{ color:#FFFFFF; white-space:nowrap; font-size:15px; line-height:16px; font-weight:bold; text-align:center; display:inline-block; padding:2px 8px; min-width:30px;}
191
+ #wpf-rating-table .wpf-badge-full.wpf-badge-level-6, #wpf-rating-table .wpf-badge-full.wpf-badge-level-7, #wpf-rating-table .wpf-badge-full.wpf-badge-level-8 { font-size:18px!important; }
192
+ #wpf-rating-table .wpf-badge-full.wpf-badge-level-9, #wpf-rating-table .wpf-badge-full.wpf-badge-level-10 { font-size:22px!important; }
193
+ #wpf-rating-table th{ text-transform:uppercase; font-size:12px;}
194
+
195
+ /**************************/
196
+ /* SETTINGS > STYLES ******/
197
+ /**************************/
198
+
199
+ .wpforo-style-color-wrapper{width:14%; float:left; }
200
+ .wpforo-style-field{ height:auto; min-height:38px;}
201
+ .wpforo-style-field input{}
202
+ .wp-picker-container input.wp-color-picker[type="text"]{ vertical-align:top; }
203
+ .wpf-forum-additional-options .wp-picker-container .wp-color-result.button,
204
+ .wpforo-style-color-wrapper .wp-picker-container .wp-color-result.button{min-height: 25px;}
205
+ .wpforo-style-color{ display:block; color:#333333; font-weight:bold; text-align:center; float:left; width:23px; padding:0 5px; margin-right:5px; border:#CCCCCC 1px solid;}
206
+ #wpf-admin-wrap .iris-picker{z-index: 1;position: absolute;}
207
+ #dynamic-css-notice-wrap{margin-top: 25px;padding: 15px;background-color: #FFFFC6;border: 1px solid #e5e5e5;}
208
+ #dynamic-css-notice-wrap p{margin: 0;}
209
+ #dynamic-css-notice-wrap br{display: block;margin: 5px 0;content: " ";line-height: 5px;}
210
+ #dynamic-css-notice-wrap hr{border: 0;border-top: 1px solid #777;border-bottom: none;}
211
+ #dynamic-css-notice-wrap b{font-size: larger;}
212
+ #dynamic-css-notice-wrap #dynamic-css-code-wrap{position: relative;padding-top: 30px;margin-top: 15px;}
213
+ #dynamic-css-notice-wrap #dynamic-css-code-wrap .dynamic-css-fname{display: inline-block;padding: 2px 15px;height: 27px;font-weight: bold;position: absolute;top: 0;left: 0;cursor: copy;background-color: lightyellow;border: 1px solid #ddd;border-bottom: none;border-top-left-radius: 5px;border-top-right-radius: 5px;box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07);-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}
214
+ #dynamic-css-notice-wrap #dynamic-css-code-wrap textarea.dynamic-css-code{width: 100%;min-height: 200px;height: 200px;resize: vertical;cursor: copy;background-color: lightyellow;color: #000;padding: 22px;}
215
+ #dynamic-css-notice-wrap #dynamic-css-code-wrap textarea.dynamic-css-code:focus{border: 1px solid #ddd;box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07);}
216
+ #dynamic-css-notice-wrap #dynamic-css-code-wrap textarea.dynamic-css-code:focus ~ .dynamic-css-fname{border-color: #5b9dd9;box-shadow: 0 0 2px rgba( 30, 140, 190, 0.8 );}
217
+ #dynamic-css-notice-wrap #dynamic-css-code-wrap .wpf_copied_txt{display: inline-block;align-items: center;transition: opacity 0.2s linear;justify-content: center;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);padding: 3px 15px;border-radius: 7px;background: rgba(0, 0, 0, 0.8);opacity: 0;color: #ffffff;font-weight: bold;font-size: 1.2em;}
218
+ .wpf_copy_animate{animation: wpf_bounce 0.4s linear;}
219
+ #dynamic-css-notice-wrap #dynamic-css-code-wrap.wpf_copy_animate .wpf_copied_txt{opacity: 1;}
220
+ @keyframes wpf_bounce {
221
+ 0%{
222
+ transform: scale(1);
223
+ }
224
+ 20%{
225
+ transform: scale(0.95);
226
+ }
227
+ 40%{
228
+ transform: scale(1.04);
229
+ }
230
+ 60%{
231
+ transform: scale(0.97);
232
+ }
233
+ 80%{
234
+ transform: scale(1.02);
235
+ }
236
+ 100%{
237
+ transform: scale(1);
238
+ }
239
+ }
240
+
241
+ .wpf-color-desc ul{
242
+ margin-left: 10px;
243
+ list-style: disc;
244
+ padding-top: 5px;
245
+ }
246
+ .wpf-color-desc ul li{
247
+ padding: 0;
248
+ line-height: 18px;
249
+ list-style: disc;
250
+ font-size: 14px;
251
+ }
252
+ .wpf-color-desc ul li ul li{
253
+ list-style: circle;
254
+ }
255
+ /**************************/
256
+ /* THEMES *****************/
257
+ /**************************/
258
+
259
+ .wpforo-themes .wpf-div-table{ display:table!important; border: 1px solid #e5e5e5; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); background:#FFFFFF; width:100%; padding:0 10px 10px 10px; margin-top:30px; box-sizing:border-box;}
260
+ .wpforo-themes .wpf-div-tr{ display:table-row; }
261
+ .wpforo-themes .wpf-div-td{ display:table-cell; vertical-align:top; padding:15px 10px 10px 15px; }
262
+ .wpforo-themes .wpf-theme-screenshot{ width:250px; height:180px; border:3px solid #ddd; float:left; }
263
+ .wpforo-themes .wpf-theme-info{ margin-left:270px; min-height:185px; }
264
+ .wpforo-themes .wpf-theme-info p{ margin:1px;}
265
+ .wpforo-themes .wpf-layout-info p{ margin:1px;}
266
+ .wpforo-themes .wpf-layout-info a{ text-decoration:none; font-weight:normal; }
267
+ .wpforo-themes .wpf-theme-actions{ display:block; clear:both; margin-top:10px; text-align:right; box-sizing:border-box; padding:2px 20px; }
268
+ .wpforo-themes .wpf-theme-actions .wpf-action{ }
269
+
270
+ /**************************/
271
+ /* ADDONS *****************/
272
+ /**************************/
273
+ #menu-comments ul > li:nth-child(3) a{ color:#25ACEA!important; text-shadow: 1px 1px 1px #000; }
274
+ .wpforo-addons .wpforo-addons-wrapper {box-sizing: border-box;}
275
+ .wpforo-addons .wpforo-addon-block {width: 280px; padding-top:10px; height:auto; float: left; margin-right: 1%; margin-bottom: 20px; background: #fff; border: 2px solid #e5e5e5; box-shadow: 0 3px 3px rgba(0, 0, 0, 0.04);}
276
+ .wpforo-addons .wpforo-addon-block .addon-thumb {float:none; width: 260px; padding:10px; margin-top:2px; height:180px;}
277
+ .wpforo-addons .wpforo-addon-block .addon-info {display:block;background:#43A6DF; padding:10px 15px; border:3px solid #fff; border-top:none;margin: 10px 0 0;min-height: 105px;}
278
+ .wpforo-addons .wpforo-addon-block .addon-title { font-size:16px; font-weight:bold; color:#FFFFFF; margin:0; line-height:18px; display:inline-block; text-decoration:none;}
279
+ .wpforo-addons .wpforo-addon-block .addon-title:hover { color:#E5F6FF;}
280
+ .wpforo-addons .wpforo-addon-block .addon-desc { font-size:13px; color:#FFFFFF; margin:0; line-height:18px; padding:10px 0 0 0; font-style:italic;}
281
+ .wpforo-addons .wpforo-addon-block .addon-isactive {margin: 0; padding:10px; background:#e5e5e5; text-align:center;}
282
+ .wpforo-addons .wpforo-addon-block .addon-button{ margin-bottom:10px;}
283
+ .wpforo-addons .wpforo-addon-block .note-installed { padding:10px 20px; background:#fff; border: 1px solid #ddd; box-shadow: 0 3px 3px rgba(0, 0, 0, 0.04); color:#2396D3; font-size:14px; margin-top:20px; }
284
+
285
+ /**************************/
286
+ /* TOOLS ******************/
287
+ /**************************/
288
+ #wpf-admin-wrap .wpf-tool-box { border:1px solid #ddd; min-height:350px; float:left; margin-right:1%; margin-bottom:10px; padding:10px 10px 20px 10px; box-sizing:border-box; width:100%;}
289
+ #wpf-admin-wrap .wpf-tool-box h3{ margin:0; font-weight:normal; border-bottom:1px dotted #ccc; padding-bottom:5px;}
290
+ #wpf-admin-wrap .wpf-tool-box th{ padding:5px 5px 5px 5px; font-size:13px; text-align:left; font-weight:400; line-height:16px; border-bottom:1px solid #EDEDED;}
291
+ #wpf-admin-wrap .wpf-tool-box td{ padding:5px 5px 5px 5px; border-bottom:1px solid #EDEDED;}
292
+ #wpf-admin-wrap .wpf-tool-box.wpf-spam-attach.right-box { width:52%; min-width:500px;max-height:370px; overflow-y:auto; }
293
+ #wpf-admin-wrap .wpf-tool-box.wpf-spam-attach.left-box { width:45%; min-width:300px;}
294
+ #wpf-admin-wrap .wpf-tool-box.wpf-spam-attach .wpf-spam-attach-dir{ color:#4742C5; display:block; text-align:right; }
295
+ #wpf-admin-wrap .wpf-tool-box tr:nth-child(even){ background:#EDEDED;}
296
+ #wpf-admin-wrap .wpf-tool-box .wpf-spam-item{}
297
+ #wpf-admin-wrap .wpf-tool-box .wpf-actions{}
298
+ @media screen and (max-width:1250px) {
299
+ #wpf-admin-wrap .wpf-spam-attach.left-box { width:100%; min-width:100%; min-height:unset; max-height:none!important;}
300
+ #wpf-admin-wrap .wpf-spam-attach.right-box { width:100%; min-width:100%; min-height:unset; max-height:none!important;}
301
+ }
302
+
303
+ #wpf-admin-wrap .wpf-data { word-break: break-all; background: #ffffff;border: 1px solid #aaa;margin: 2px;padding: 1px 10px;float: left;font-size: 12px;line-height: 18px;}
304
+ #wpf-admin-wrap .wpf-data > key{font-weight: 600;color: #000088;}
305
+ #wpf-admin-wrap .wpf-data > value{font-weight: 600;color: #666666;}
306
+ #wpf-admin-wrap .wpf-sb-data > key{color: #0a6d9b;}
307
+ #wpf-admin-wrap .wpf-sb-data > value{color:#1b6e08;}
308
+ #wpf-admin-wrap .wpf-main-table-data{margin: 10px;}
309
+ #wpf-admin-wrap .wpf-main-table-data td{vertical-align: top;text-align: left;padding: 0 0 15px;}
310
+ #wpf-admin-wrap .wpf-table-data{border-collapse: collapse;margin: 0;}
311
+ #wpf-admin-wrap .wpf-table-data.wpf-tbl{min-width: 550px;}
312
+ #wpf-admin-wrap .wpf-table-data tr{border: none;}
313
+ #wpf-admin-wrap .wpf-table-data td{padding: 1px 5px;font-size: 12px;border: 1px solid #cccccc;line-height: 18px;}
314
+
315
+ @keyframes dashicons-spin {
316
+ 0% {
317
+ transform: rotate( 0deg );
318
+ }
319
+ 100% {
320
+ transform: rotate( 360deg );
321
+ }
322
+ }
323
+
324
+
325
+ body{
326
+ position: relative !important;
327
+ }
328
+
329
+ #wpf-admin-loading-extrawrap{
330
+ position: fixed;
331
+ top: 0;
332
+ right: 0;
333
+ bottom: 0;
334
+ left: 0;
335
+ background-color: rgba(67, 166, 223, 0.6);
336
+ z-index: 9999999999999999;
337
+ }
338
+
339
+ #wpf-admin-loading-extrawrap .wpf-admin-loading-wrap{
340
+ display: flex;
341
+ flex-wrap: nowrap;
342
+ flex-direction: column;
343
+ position: fixed;
344
+ left: 50%;
345
+ top: 50%;
346
+ transform: translate(-50%, -50%);
347
+ transform-origin: center;
348
+ width: auto;
349
+ height: auto;
350
+ }
351
+ #wpf-admin-loading-extrawrap .wpf-admin-loading-wrap .wpf-admin-loading-ico{animation: dashicons-spin 2s infinite;animation-timing-function: linear;width: auto;height: auto;text-shadow: 0 0 30px #00000087;}
352
+ #wpf-admin-loading-extrawrap .wpf-admin-loading-wrap .wpf-admin-loading-ico::before{font-size: 100px;}
353
+ #wpf-admin-loading-extrawrap .wpf-admin-loading-wrap .wpf-admin-loading-txt{
354
+ font-size: 2.5em;
355
+ font-weight: 900;
356
+ text-shadow: 0 0 30px #00000087;
357
+ -webkit-touch-callout: none; /* iOS Safari */
358
+ -webkit-user-select: none; /* Safari */
359
+ -khtml-user-select: none; /* Konqueror HTML */
360
+ -moz-user-select: none; /* Old versions of Firefox */
361
+ -ms-user-select: none; /* Internet Explorer/Edge */
362
+ user-select: none; /* Non-prefixed version, currently supported by Chrome, Opera and Firefox */
363
+ }
364
+ #wpf-access-table #wpf-access-list tr{background-color: #FFFFFF;}
365
+ #wpf-access-table #wpf-access-list tr:nth-child(odd){background-color: #FCFCFC;}
366
+ #wpf-admin-wrap .wpf-box-wrap{margin:0; display: flex; justify-content: flex-start; align-items: flex-start; flex-wrap: wrap;}
367
+ #wpf-admin-wrap .wpf-box-wrap .wpf-dash-box{width: 31%; margin: 10px; min-width: 450px;}
368
+ #wpf-admin-wrap .wpf-box-header{font-size: 14px;padding: 8px 12px;margin: 0;line-height: 1.4;border-bottom: 1px solid #ccd0d4;}
369
+ body[class*=forums_page_wpforo] #wpfooter{position: static;}
370
+ #wpf-admin-wrap .wpf-wn-label{ font-weight: bold; padding:0 5px; font-size: 14px;}
371
+ #wpf-admin-wrap .wpf-wn-list {margin: 0 20px; font-size: 14px;}
372
+ #wpf-admin-wrap .wpf-wn-list li{margin-bottom: 2px; list-style: disc;}
373
+
374
+
375
+ /************************************************************/
376
+ /* Dashboard ##############################################*/
377
+ /************************************************************/
378
+ #wpf-dash.wrap{padding: 10px 30px 10px 40px; margin:0; }
379
+ #wpf-dash[dir=rtl]{padding: 10px 40px 10px 30px;}
380
+ #wpf-dash a{text-decoration: none;}
381
+ #wpf-dash a:hover{text-decoration: underline;}
382
+ #wpf-dash .wpf-dash-head{display: flex; flex-direction: row; justify-content: space-between; align-items: flex-start;}
383
+ #wpf-dash .wpf-dash-head-left h1{font-size: 34px; letter-spacing: 1px; line-height: 40px; padding-bottom: 0; margin-bottom: 0;}
384
+ #wpf-dash .wpf-dash-head-left .wpf-head-subtitle{margin-left: 275px; font-size: 11px; color: #0e8e75; font-weight: bold; font-family: "Courier New", Courier, monospace; margin-top: -1px;}
385
+ #wpf-dash .wpf-dash-head-left .wpf-head-welcome{font-size: 15px; line-height: 25px; letter-spacing: 1px; font-family: 'Lucida Bright', 'DejaVu Serif', Georgia, serif; padding-top: 15px;}
386
+ #wpf-dash .wpf-dash-head-right{text-align: center;}
387
+ #wpf-dash .wpf-dash-head-right img{ width: 147px; display: block; margin: 10px auto 1px auto;}
388
+ #wpf-dash .wpf-dash-head-right .wpf-version{font-family: Verdana, Arial, sans-serif; font-weight: bold; font-size: 12px; color: #666666; }
389
+ #wpf-dash .wpf-section{margin: 35px 0 20px 0; display: flex; flex-direction: row; justify-content: space-between; align-items: flex-end; width: 100%; border-bottom: 1px solid #cccccc; padding: 0 1px 8px 1px; }
390
+ #wpf-dash .wpf-section h3{padding: 0; margin: 0;}
391
+ #wpf-dash .wpf-section .wpf-close{font-size: 14px; color: #666666; cursor: pointer;}
392
+ #wpf-dash .wpf-section .dashicons{color: #777777; cursor: pointer;}
393
+ #wpf-dash .wpf-sec-stat{margin-top: 30px;}
394
+ #wpf-dash .wpf-box{background: #FFF; box-shadow: 0 0 3px #aaa;}
395
+ #wpf-dash .wpf-box .wpf-box-head{border-bottom: 1px solid #ccc; padding: 10px 20px; display: flex; flex-direction: row; justify-content: space-between; align-items: center;}
396
+ #wpf-dash .wpf-box .wpf-box-head h3{font-size: 16px; margin: 0; padding: 0; line-height: 24px; color: #333333; font-weight: normal;}
397
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-toggle{width: 50px; text-align: right; display: flex; flex-direction: row;justify-content: flex-end; align-items: center;}
398
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-toggle span{font-size: 22px; color: #777; cursor: pointer; display: inline; line-height: 22px;}
399
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-toggle span.dashicons-admin-generic{font-size: 15px;}
400
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-toggle span.dashicons-arrow-left,
401
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-toggle span.dashicons-arrow-right{line-height: 22px; width: 14px; font-size: 24px; color: #0d9b7f; margin-right: 3px;}
402
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-info{flex-grow: 1; padding-left: 20px;}
403
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-info span{font-size: 12px; color: #666;}
404
+ #wpf-dash .wpf-box .wpf-box-head .wpf-box-info span.dashicons{font-size: 13px; line-height: 21px;}
405
+
406
+
407
+ /************************************************************/
408
+ /* Board Edit Page #########################################*/
409
+ /************************************************************/
410
+
411
+ .wpf-board{display: flex; justify-content: space-between; align-items: flex-start; margin-top: 50px;}
412
+ .wpf-board-left{width: calc(100% - 270px);}
413
+ .wpf-board-right{width: 270px; margin-left: 20px; background: #fff; padding: 20px;}
414
+ .wpf-board-option{margin-bottom: 20px;}
415
+ .wpf-board-option label{ font-size: 16px; font-weight: 500;}
416
+ .wpf-board-option .wpf-board-field{margin-top: 5px;}
417
+ .wpf-board-option .wpf-board-field input[type="text"],
418
+ .wpf-board-option .wpf-board-field textarea{width: 100%; background: #fff; padding: 0px 10px;}
419
+
420
+ .wpf-board-box{background: #fff; padding: 20px 20px 10px; display: flex; justify-content: flex-start; align-items: stretch; flex-wrap: wrap; margin-bottom: 25px;}
421
+ .wpf-board-box div{margin-right: 20px; border-right: 1px dashed #ccc; padding-right: 20px; width: 32%; min-width: 320px; margin-bottom: 20px;}
422
+ .wpf-board-box div:last-child{border-right: none;}
423
+ .wpf-board-box div > label{display: block; font-size: 15px; font-weight: 500; margin-bottom: 7px;}
424
+ .wpf-board-box div > label .wpfb-desc{font-size: 13px; font-weight: normal; display: block;}
425
+
426
+ .wpf-board-side-box{ padding-bottom: 20px; border-bottom: 3px solid #f5f5f5; margin-bottom: 20px; }
427
+ .wpf-board-side-box > label{ font-size: 15px; font-weight: 500; display: block; padding-bottom: 7px;}
428
+ .wpf-board-submit .button{width: 100%; font-size: 16px;}
429
+
430
+ .wpf-board-modules{background: #fff; padding: 20px 20px 10px; display: flex; justify-content: flex-start; align-items: stretch; flex-wrap: wrap; margin-bottom: 25px;}
431
+ .wpf-module-type{width: 100%; font-size: 20px; font-weight: normal; border-bottom: 1px solid #eee; margin-bottom: 10px; padding: 10px}
432
+ .wpf-board-module{margin: 0.5%; padding: 10px 0 15px; box-shadow: 0 0 20px 1px #f5f5f5 inset; min-width: 280px; width: 18%; border: 1px dashed #ddd; display: flex; justify-content: flex-start; align-items: stretch;}
433
+ .wpf-board-module .wpf-module-head{width: 84px; text-align: center; display: flex; justify-content: center; align-items: center;}
434
+ .wpf-board-module .wpf-module-head svg{width: 48px; height: 48px; vertical-align: middle; fill: #43A6DF;}
435
+ .wpf-board-module .wpf-module-title{min-height: 32px; font-size: 13px; font-weight: 500; display: flex; justify-content: flex-start; align-items: center;}
436
+ .wpf-board-module .button{margin-top: -3px;}
437
+
438
+ /************************************************************/
439
+ /* Setting Boxes ###########################################*/
440
+ /************************************************************/
441
+
442
+ #wpcontent[dir=rtl]{padding-right: 0;}
443
+ #wpf-setbox{box-sizing:border-box; padding: 0; }
444
+ #wpf-setbox[dir=rtl] { margin-right: -10px;}
445
+ #wpf-setbox.wrap{}
446
+ #wpf-setbox a{text-decoration: none;}
447
+ #wpf-setbox a:hover{text-decoration: underline;}
448
+ #wpf-setbox a:visited, #wpf-setbox a:focus{box-shadow: none!important;}
449
+ #wpf-setbox .wpf-setbox-head{box-sizing:border-box; padding: 20px 30px 20px 30px; margin-top: 18px; margin-bottom: 40px; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; align-items: center; box-shadow: 0 0 10px #ccc; background-color: #f9f9f9; width: calc(100% - 16px)}
450
+ #wpf-setbox .wpf-setbox-head .wpf-head-logo{width: 200px;}
451
+ #wpf-setbox .wpf-setbox-head .wpf-head-logo img{height: 40px;}
452
+ #wpf-setbox .wpf-setbox-head .wpf-head-title{text-shadow: 1px 1px 1px #b9b9b9; flex-grow: 1; padding: 0 15px 10px 15px; font-size: 30px; font-weight: 600; color: #43A6DF; letter-spacing: 1px;}
453
+ #wpf-setbox .wpf-setbox-head .wpf-head-info{ font-size: 14px;}
454
+ #wpf-setbox .wpf-setbox-head .wpf-head-info a{overflow: hidden; outline: none; font-weight: 600; opacity: 0.8;}
455
+ #wpf-setbox .wpf-setbox-head .wpf-head-info span{padding: 0 10px; border-right: 1px solid #0073aa;}
456
+ #wpf-setbox .wpf-setbox-head .wpf-head-info span:last-child{border-right: none;}
457
+ #wpf-setbox .wpf-setbox-head .wpf-head-info span[dir=rtl]{border-left: 1px solid #999;}
458
+ #wpf-setbox .wpf-setbox-head .wpf-head-info span:last-child[dir=rtl]{border-left: none;}
459
+ #wpf-setbox .wpf-setbox-body { padding: 0 15px; }
460
+ #wpf-setbox .wpf-setbox-body .wpf-section{border-bottom: 1px solid #ccc; padding: 8px 0; display: flex; flex-direction: row; justify-content: space-between; align-items: flex-end;}
461
+ #wpf-setbox .wpf-setbox-body .wpf-section h3{letter-spacing:1px; font-weight: normal; color: #444; font-size: 20px; margin: 0; padding: 0; line-height: 24px;}
462
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search{position: relative; text-align: right; z-index: 999; width: 30%;}
463
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search input[type="text"]{max-width: none; background: #fafafa;}
464
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search[dir=rtl]{text-align: left;}
465
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search #wpf-opt-search-results{display:none;box-sizing:border-box;position: absolute;width: 100%;text-align: left;z-index: 99;border: 1px solid #ccc;background-color: #fff;left: 1px;top: 30px;}
466
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search #wpf-opt-search-results a{padding: 5px;display: inline-block;text-decoration: none;width: 97%;}
467
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search #wpf-opt-search-results a:hover, #wpf-setbox .wpf-setbox-body .wpf-opt-search #wpf-opt-search-results a:focus{-webkit-transition: color .3s linear;-moz-transition: color .3s linear;-o-transition: color .3s linear;-ms-transition: color .3s linear;transition: color .3s linear;color: #00B38F;}
468
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search #wpf-opt-search-results a.wpf-opt-search-tabtitle{margin-left: 5px; width: 90%; font-weight: 600; font-size: 14px;}
469
+ #wpf-setbox .wpf-setbox-body .wpf-opt-search #wpf-opt-search-results a.wpf-opt-search-taboption{margin-left: 20px;width: 90%; font-size: 13px;}
470
+ #wpf-setbox .wpf-setbox-body .dashicons-search{display: inline-block; position: absolute; bottom: 4px; right: 4px; font-size: 20px; color: #43a6df; cursor: pointer;}
471
+ #wpf-setbox .wpf-setbox-body .dashicons-search[dir=rtl]{right: auto; left: 4px;}
472
+ #wpf-setbox .wpf-setbox-body input[type="text"]{font-size: 15px; line-height: 20px; padding: 5px 10px; display: inline-block; width: 100%; max-width: 400px;}
473
+ #wpf-setbox .wpf-setbox-body .wpf-box-wrap{display: block; align-items: center; padding: 25px 0 10px 0; }
474
+ #wpf-setbox .wpf-setbox-body .wpf-box{background: #FFF; box-shadow: 0 0 2px #bbb; position: relative; float: left;}
475
+ #wpf-setbox .wpf-setbox-body .wpf-box-info{ width: 0; height: 0; right: 0; top: 0; position: absolute; border-left: 41px solid transparent;}
476
+ #wpf-setbox .wpf-setbox-body .wpf-box-info[dir=rtl]{ right: auto; left: 0;}
477
+ #wpf-setbox .wpf-setbox-body .wpf-box-info img{position: absolute; top: -36px; right: 3px; z-index: 10; height: 16px;}
478
+ #wpf-setbox .wpf-setbox-body .wpf-box-info img[dir=rtl]{left: 3px;}
479
+ #wpf-setbox .wpf-setbox-body .wpf-box-info.wpf-ok img{height: 17px;}
480
+ #wpf-setbox .wpf-setbox-body .wpf-box-info.wpf-new img{height: 8px; top: -32px; right: 1px;}
481
+ #wpf-setbox .wpf-setbox-body .wpf-box-info.wpf-ok{border-top: 41px solid #43a6df;}
482
+ #wpf-setbox .wpf-setbox-body .wpf-box-info.wpf-new{border-top: 41px solid #8e58a3;}
483
+ #wpf-setbox .wpf-setbox-body .wpf-box-info.wpf-note{border-top: 41px solid #f98e3c;}
484
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-head{display: flex; justify-content: center; align-items: center; text-align: center; padding: 5px 5px 0 5px; height: 90px;}
485
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-head a{ display: flex; width: 100%; height: 100%; justify-content: center; align-items: center;}
486
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-head svg{ color: #43a6df; fill: #43a6df;}
487
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot{min-height: 70px; background-color: #fafafa; display: flex; flex-direction: row; justify-content: space-between; align-items: stretch; border-top: 1px solid #e5e5e5;}
488
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot:hover{background-color: #f1f1f1;}
489
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-title{display: flex; flex-wrap: wrap; justify-content: center; align-items: center; text-align: center; flex-grow: 1; padding: 5px 20px; box-sizing: border-box;}
490
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-title a{font-size: 15px;color: #555;line-height: 19px;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;}
491
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-title a:hover{text-decoration: none;}
492
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-arrow{display: flex; justify-content: center; align-items: center; text-align: center; padding-right: 20px; box-sizing: border-box;}
493
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-arrow[dir=rtl]{padding-left: 20px;}
494
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-arrow img{height: 24px;}
495
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-body{display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 12px 10px;}
496
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-body .wpf-title{font-size: 15px; flex-grow: 1; padding: 0 10px; color: #666; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;}
497
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-body .wpf-title a{font-size: 15px; color: #555; line-height: 19px;}
498
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-body .wpf-title a:hover{text-decoration: none;}
499
+ #wpf-setbox .wpf-setbox-body .wpf-box .wpf-body:hover{background-color: #fafafa;}
500
+ #wpf-setbox .wpf-box-wrap.wpf-box-addons{justify-content: flex-start;}
501
+ #wpf-setbox .wpf-box-wrap.wpf-box-addons .wpf-box{margin-bottom: 10px;}
502
+ .wpf-clear{clear: both;}
503
+
504
+ /************************************************************/
505
+ /* Single Settings #########################################*/
506
+ /************************************************************/
507
+ #wpf-setbox .wpf-switcher input[type=checkbox]{ height: 0; width: 0; visibility: hidden; position: absolute;}
508
+ #wpf-setbox .wpf-switcher label { cursor: pointer; text-indent: -9999px; width: 50px; height: 18px; background: grey; display: block; border-radius: 30px; position: relative;border: 1px solid rgba(0, 0, 0, 0.1); -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px rgba(255, 255, 255, 0.1); -webkit-transition: all 0.1s ease-in-out; -moz-transition: all 0.1s ease-in-out; -ms-transition: all 0.1s ease-in-out; -o-transition: all 0.1s ease-in-out; transition: all 0.1s ease-in-out;}
509
+ #wpf-setbox .wpf-switcher label:after {content: ''; position: absolute; top: 4px; left: 5px; width: 10px; height: 10px; background: #fff; border-radius: 90px; transition: 0.3s;}
510
+ #wpf-setbox .wpf-switcher input:checked + label {background: #43A6DF;}
511
+ #wpf-setbox .wpf-switcher input:checked + label:after {left: calc(100% - 5px);transform: translateX(-100%);}
512
+ #wpf-setbox .wpf-switcher label:active:after {width: 12px;}
513
+ #wpf-setbox .wpf-radio input[type=radio]{display: none;}
514
+ #wpf-setbox .wpf-radio .wpf-radio-circle {cursor: pointer; width: 16px; height: 16px; background: grey; display: inline-block; border-radius: 30px; position: relative;border: 1px solid rgba(0, 0, 0, 0.1); -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px rgba(255, 255, 255, 0.1);}
515
+ #wpf-setbox .wpf-radio input:checked + .wpf-radio-circle {background: #43A6DF;}
516
+ #wpf-setbox .wpf-radio input:checked + .wpf-radio-circle:after {content: ''; width: 8px; height: 8px; margin: 4px; background: #fff; border-radius: 50%; display: inline-block;}
517
+ #wpf-setbox .wpf-desc{font-size:13px; color:#666666; font-style: italic; width:90%; padding:5px 0 0 0; margin:0; line-height: 18px; font-weight: normal;}
518
+ #wpf-setbox .wpf-setbox-body .wpf-setcon{width: calc(100% - 350px); padding-bottom: 20px; float: left;}
519
+ #wpf-setbox .wpf-setbox-body .wpf-setbar{width: 320px; float: right;}
520
+ #wpf-setbox .wpf-setcon .wpf-setcon-head{padding: 14px 30px; font-size: 16px; font-weight: 600; line-height: 21px; border-bottom: 1px dashed #43A6DF; color: #43A6DF; margin-bottom: 5px; display: flex; justify-content: space-between; align-items: center; flex-direction: row;}
521
+ #wpf-setbox .wpf-setcon .wpf-setcon-head a.wpf-back{font-size: 14px; line-height: 20px;}
522
+ #wpf-setbox .wpf-setcon .wpf-setcon-head a.wpf-back:hover{text-decoration:none;}
523
+ #wpf-setbox .wpf-setcon .wpf-setcon-head a.wpf-back .dashicons{font-size: 14px; line-height: 22px;}
524
+ #wpf-setbox .wpf-setcon .wpf-setcon-body{padding: 0 30px;}
525
+ #wpf-setbox .wpf-setcon .wpf-opt-row{display: flex; justify-content: space-between; align-items: flex-start; padding: 15px 0 10px 0; border-bottom: 1px solid #ddd;}
526
+ #wpf-setbox .wpf-setcon .wpf-opt-row:last-child{border-bottom: none;}
527
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-opt-row-tight{padding-top: 8px; padding-bottom: 8px;}
528
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-intro svg{float: left; padding: 0 30px 5px 10px; fill: #43A6DF; color: #43A6DF;}
529
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-intro svg[dir=rtl]{float: left; padding: 0 10px 5px 30px}
530
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-intro img{float: left; padding: 0 30px 5px 10px;}
531
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-intro img[dir=rtl]{float: left; padding: 0 10px 5px 30px}
532
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-name{font-size: 15px; width: calc(60% - 42px); color: #555; font-weight: 600;}
533
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-input{width: 40%; min-width: 300px;}
534
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-input .wpf-switcher{ width: 60px;}
535
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-doc{width: 40px; text-align: right; font-size: 16px;}
536
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-input .wpf-optset{display: flex; justify-content: flex-start; align-items: center; margin-bottom: 6px;}
537
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-input .wpf-optset .wpf-switcher{margin: 0;}
538
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-input .wpf-optset .wpf-optset-label{padding: 0 10px 2px 10px; color: #777777;}
539
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-intro{width: calc(100% - 40px); font-size: 15px; line-height: 22px; padding: 5px 0 20px 0; text-align: justify;}
540
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper{flex-wrap:wrap;}
541
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-opt-input{width:100%;}
542
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group{margin-top:10px;display:flex;align-items:center;justify-content:space-between;}
543
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group.wpf-custom-option-group-default{display:none !important;}
544
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group .wpf-custom-option-label{width: 28%;}
545
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group .wpf-custom-option-value{width: 68%;}
546
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group .dashicons.dashicons-trash{color:#e51c23;cursor:pointer;}
547
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group-add{display:flex;align-items:center;justify-content:center;color:#43A6DF;background-color:#f5f5f5;margin:10px 0;height: 20px;font-size:23px;padding:15px;cursor:pointer;}
548
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group-add:hover{color:#f5f5f5;background-color:#43A6DF;}
549
+ #wpf-setbox .wpf-setcon .wpf-opt-row.wpf-custom-option-group-wrapper .wpf-custom-option-group-add .dashicons.dashicons-plus{font-size:40px;width:40px;height:35px;}
550
+ #wpf-setbox .wpf-setcon-body .wpf-opt-row input[type="submit"]{margin-left: 10px;}
551
+ #wpf-setbox .wpf-setcon-body .wpf-opt-row-textarea .wpf-opt-name{ width: 39%; }
552
+ #wpf-setbox .wpf-setcon-body .wpf-opt-row-textarea .wpf-opt-input{ width: 60%; text-align: right; }
553
+ #wpf-setbox .wpf-setcon-body .wpf-opt-row-textarea textarea{ width: 100%; min-height: 120px; color: #333333; font-family: 'Courier New', Courier, monospace; background: #f5f5f5; }
554
+ #wpf-setbox .wpf-setcon-body .wpf-opt-row-checkbox .wpf-opt-checkbox-wrap{ min-width: 45%; width: 48%; margin: 3px 1%; }
555
+ #wpf-setbox .wpf-setcon-body .wpf-opt-row-checkbox .wpf-opt-input{ padding: 0; display: flex; justify-content: flex-start; align-items: flex-start; flex-wrap: wrap; }
556
+ #wpf-setbox .wpf-setcon-body .wpforo-opt-richeditor tr td:first-of-type{ padding: 10px 0; width: 35%; }
557
+ #wpf-setbox .wpf-setcon-body .wpforo-opt-richeditor tr td label{ font-size: 15px; color: #555; font-weight: 600; }
558
+ /*#wpf-setbox .wpf-setcon-body .wpf-opt-input .wpf-opt-name,
559
+ #wpf-setbox .wpf-setcon-body .wpf-opt-input .wpf-opt-doc{ display: none; }*/
560
+ #wpf-setbox .wpf-setcon-body .wpf-opt-input .wpf-opt-input { flex-grow: 1; }
561
+ #wpf-setbox .wpf-setcon-body .wpf-opt-input .wpf-opt-row { border-bottom: 0; }
562
+ #wpf-setbox .wpf-setbar .wpf-box.wpf-menu-group{width: 100%; }
563
+ #wpf-setbox .wpf-setbar .wpf-box.wpf-menu-group:first-child{margin-bottom: 20px;}
564
+ #wpf-setbox .wpf-setbar ul{width: 100%; margin: 0; padding: 0; box-sizing: border-box;}
565
+ #wpf-setbox .wpf-setbar ul[dir=rtl]{border-right: 1px solid #43A6DF;}
566
+ #wpf-setbox .wpf-setbar li{width: 100%; padding: 8px 10px; margin: 0; font-size: 13px; font-weight: 600; box-sizing: border-box; border-top: 1px solid #ddd; }
567
+ #wpf-setbox .wpf-setbar li:nth-child(2){border-top: none;}
568
+ #wpf-setbox .wpf-setbar li .dashicons-arrow-left-alt2{visibility: hidden; font-size: 16px; line-height: 23px;}
569
+ #wpf-setbox .wpf-setbar li:hover .dashicons-arrow-left-alt2{visibility: visible;}
570
+ #wpf-setbox .wpf-setbar li a{color: #666666; display: block; line-height: 20px; outline: 0;}
571
+ #wpf-setbox .wpf-setbar li a:hover{color: #43A6DF; text-decoration: none;}
572
+ #wpf-setbox .wpf-setbar li a:visited, #wpf-setbox .wpf-setbar li a:focus{outline: 0; box-shadow: none!important;}
573
+ #wpf-setbox .wpf-setbar li.wpf-active{margin-left: 0;color: #fff;background-color: #43A6DF; font-size: 14px;width: calc(100% + 1px); }
574
+ #wpf-setbox .wpf-setbar li.wpf-active a{color: #ffffff;}
575
+ #wpf-setbox .wpf-setbar li.wpf-active .dashicons-arrow-left-alt2{visibility: visible; font-size: 20px;}
576
+ #wpf-setbox .wpf-setbar li.wpf-active[dir=rtl]{margin-right: -1px;}
577
+ #wpf-setbox .wpf-setbar li.wpf-menu-head{padding: 14px 15px; color: #43A6DF; font-weight: 600; letter-spacing: 1px; font-size: 16px; display: flex; justify-content: space-between; align-items: center; flex-direction: row; border-bottom: 1px dashed #43A6DF;}
578
+ #wpf-setbox .wpf-setbar li.wpf-menu-head .dashicons{font-size: 25px; line-height: 22px; cursor: pointer;}
579
+
580
+ #wpf-setbox .wpf-setcon .wpf-mublock,
581
+ #wpf-setbox .wpf-setcon .wpf-mublock-inline{margin-bottom:5px;}
582
+ #wpf-setbox .wpf-setcon .wpf-mublock-inline {float:left; display:inline-block; width:19.5%}
583
+ #wpf-setbox .wpf-setcon .wpf-mublock{display:block;}
584
+ #wpf-setbox .wpf-setcon .wpf-mublock-inline label,
585
+ #wpf-setbox .wpf-setcon .wpf-mublock label{white-space:nowrap; font-size:13px; vertical-align:top; margin-left:2px;overflow: hidden;text-overflow: ellipsis;display: inline-block;width: 70%;}
586
+ #wpf-setbox .wpf-setcon .wpf-switch-field input { display: none;}
587
+ #wpf-setbox .wpf-setcon .wpf-switch-field label {float: left; display: inline-block; line-height: 20px; min-width:80px; font-weight: normal; background-color: #e4e4e4;color:#333;font-size: 13px;text-align: center; text-shadow: none;padding: 2px 10px; border: 1px solid rgba(0, 0, 0, 0.2);-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, 255, 0.1);box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, 255, 0.1);-webkit-transition: all 0.1s ease-in-out;-moz-transition: all 0.1s ease-in-out; -ms-transition: all 0.1s ease-in-out; -o-transition: all 0.1s ease-in-out;transition: all 0.1s ease-in-out;}
588
+ #wpf-setbox .wpf-setcon .wpf-switch-field label:hover {cursor: pointer;}
589
+ #wpf-setbox .wpf-setcon .wpf-switch-field input:checked + label {background-color: #43A6DF; -webkit-box-shadow: none; box-shadow: none; color:#fff;} /* #66DD8D */
590
+ #wpf-setbox .wpf-setcon .wpf-switch-field label:first-of-type {border-radius: 4px 0 0 4px;}
591
+ #wpf-setbox .wpf-setcon .wpf-switch-field label:last-of-type {border-radius: 0 4px 4px 0;}
592
+ #wpf-setbox .wpf-setcon .wpf-color-wrap{padding: 7px 5px; width: 300px; min-width: 50%; float: left; box-sizing: border-box;}
593
+ #wpf-setbox .wpf-setcon .wpf-color-wrap input{width: 80px!important; padding: 3px 5px!important; font-size: 13px!important; text-align: center!important;}
594
+ #wpf-setbox .wpf-setcon .wpf-color-wrap label{font-weight: normal!important; display: inline-block!important; margin: 0 5px!important;}
595
+ #wpf-setbox .wpf-setcon h2.wpf-subtitle{padding: 40px 0 10px 0; margin: 0; font-size: 17px; font-weight: normal;color: #43A6DF; border-bottom: 1px dashed #43A6DF;}
596
+ #wpf-setbox .wpf-setcon .wpf-scol-wrap{padding: 7px 5px; width: 300px; min-width: 50%; box-sizing: border-box;}
597
+ #wpf-setbox .wpf-setcon .wpf-scol-wrap input{width: 80px!important; padding: 3px 5px!important; font-size: 13px!important; text-align: center!important;}
598
+ #wpf-setbox .wpf-setcon .wpf-scol-wrap label{font-weight: normal!important; display: inline-block!important; margin: 0 5px!important;}
599
+
600
+ #wpf-setbox .wpf-setcon .wpf-opt-row-fullwidth{ flex-direction: column }
601
+ #wpf-setbox .wpf-setcon .wpf-opt-row-fullwidth .wpf-opt-name,
602
+ #wpf-setbox .wpf-setcon .wpf-opt-row-fullwidth .wpf-opt-input{ width: 100% }
603
+
604
+ div.wpf-subtitle{padding: 30px 10px 15px 2px; margin: 0; font-size: 19px; font-weight: normal;color: #43A6DF; border-bottom: 1px dashed #43A6DF;}
605
+ div.wpf-subtitle .wpd-info{color: #666; margin: 8px 0 0 0;}
606
+ div.wpf-subtitle strong{display: block; margin: 5px 0 0 0; font-size: 14px; font-weight: normal; color: #666;}
607
+ h4.wpf-subtitle{ padding: 30px 0 10px 0; margin: 0; font-size: 16px; font-weight: normal; color: #43A6DF; border-bottom: 1px dashed #43A6DF;}
608
+
609
+ /*.wpf-enabled{background-image: url('../img/editor/active.png');}*/
610
+ /*.wpf-disabled{background-image: url('../img/editor/passive.png');}*/
611
+ .wpf-editor-toolbar{display: flex; margin-top: 15px; flex-direction: row; justify-content: flex-start; flex-wrap: wrap;} /*#f06653*/
612
+ .wpf-editor-disable{cursor:pointer; margin-top:5px; background: #f5f5f5; font-size: 13px; font-weight: 600; padding: 2px 15px; line-height: 18px; border-radius: 3px;}
613
+ .wpf-editor-button{cursor:pointer; margin-top:5px; width: 30px; height: 23px; background-size:auto 23px; border: 1px solid #eee; margin-right: 5px; background-repeat: no-repeat;}
614
+ .wpf-editor-attachment{display:block; margin-top:5px; /*background-image: url('../img/editor/attachment.png');*/ width: 30px; height: 23px; background-size:auto 23px; border: 1px solid #eee; margin-right: 5px; background-repeat: no-repeat;}
615
+
616
+ #wpf-setbox .wpf-spam-attach.right-box { width:52%; min-width:500px;max-height:370px; overflow-y:auto; }
617
+ #wpf-setbox .wpf-spam-attach.left-box { width:45%; min-width:300px;}
618
+ #wpf-setbox .wpf-spam-attach .wpf-spam-attach-dir{ color:#4742C5; display:block; text-align:right; }
619
+
620
+ .iconpicker-container{
621
+ display: flex;
622
+ align-items: center;
623
+ }
624
+
625
+ .wpf-fa-ico-primary-bg{
626
+ background-color: #337AB7;
627
+ color: #FFFFFF;
628
+ }
629
+ .wpf-fa-ico-preview{
630
+ display: flex;
631
+ justify-content: center;
632
+ align-items: center;
633
+ padding: 0 10px;
634
+ background-color: #EFEFEF;
635
+ position: absolute;
636
+ right: 2px;
637
+ top: 2px;
638
+ bottom: 2px;
639
+ min-width: 40px;
640
+ }
641
+
642
+ .wpf-input-group-addon {
643
+ padding: 6px 12px;
644
+ font-size: 14px;
645
+ font-weight: 400;
646
+ line-height: 1;
647
+ color: #555;
648
+ text-align: center;
649
+ background-color: #eee;
650
+ border: 1px solid #ccc;
651
+ border-radius: 4px;
652
+ white-space: nowrap;
653
+ margin-left: -44px;
654
+ border-collapse: separate;
655
+ }
656
+
657
+ .wpfo-settings-style-colorid{
658
+ line-height: 38px;
659
+ min-height: 32px;
660
+ font-size: 14px;
661
+ font-weight: bold;
662
+ }
663
+
664
+ .wpfo-settings-style-colorid-hash{
665
+ border-bottom: 2px solid #EEEEEE;
666
+ margin-bottom: 5px;
667
+ }
668
+
669
+ .iconpicker-popover{
670
+ box-shadow: 1px 6px 26px 2px rgb(0 0 0 / 50%);
671
+ z-index: 99999;
672
+ }
673
+
674
+ #wpf-admin-wrap .wpf-selected{
675
+ background-color: rgb(246, 242, 165);
676
+ transition: background-color 0.5s ease;
677
+ }
678
+
679
+ #wpf-admin-wrap .wpf-opt-row-cover .wpf-opt-input{
680
+ width: 100% !important;
681
+ }
682
+
683
+ #wpf-admin-wrap .wpf-opt-row-cover .wpf-cover{
684
+ width: 100%;
685
+ position: relative;
686
+ height: 220px;
687
+ border-radius: 15px;
688
+ background-size: cover;
689
+ background-position: center;
690
+ }
691
+
692
+ #wpf-admin-wrap .wpf-edit-cover{
693
+ position: absolute;
694
+ right: 10px;
695
+ bottom: 10px;
696
+ font-size: 20px;
697
+ padding: 0 4px;
698
+ margin: 0;
699
+ line-height: 18px;
700
+ color: #555;
701
+ cursor: pointer;
702
+ }
703
+
704
+ #wpf-admin-wrap .wpf-edit-cover i{background-color: #ccc; opacity: 0.7; padding: 1px 2px; border-radius: 3px;}
705
+ #wpf-admin-wrap .wpf-box-wrap .wpf-box-head img{max-width: 70px; }
706
+ #wpf-admin-wrap .wpf-addon-table {
707
+ width: 100%;
708
+ border: none;
709
+ padding: 0;
710
+ margin: 0 auto;
711
+ box-sizing: border-box;
712
+ }
713
+
714
+ #wpf-admin-wrap .wpf-addon-table tr:nth-child(2n+1) {
715
+ background: #f9f9f9 none repeat scroll 0 0;
716
+ }
717
+
718
+ #wpf-admin-wrap .wpf-addon-table td,
719
+ #wpf-admin-wrap .wpf-addon-table th {
720
+ padding: 10px 15px;
721
+ text-align: left;
722
+ vertical-align: top;
723
+ }
724
+
725
+ #wpf-admin-wrap .wpf-addon-table label {
726
+ font-size: 14px;
727
+ }
728
+
729
+ #wpf-admin-wrap .wpfog {
730
+ color: #F45B00;
731
+ white-space: nowrap;
732
+ }
733
+
734
+ #wpf-admin-wrap .wpfogx {
735
+ font-weight: bold;
736
+ font-family: Gotham, "Helvetica Neue", Helvetica, Arial, sans-serif;
737
+ cursor: help;
738
+ font-size: 12px;
739
+ }
740
+
741
+ #wpf-admin-wrap .wpf-email-shortcodes {
742
+ font-weight: normal;
743
+ font-size: 12px;
744
+ color: #666666;
745
+ list-style: disc;
746
+ margin-left: 20px;
747
+ }
748
+
749
+ #wpf-admin-wrap .wpf-email-shortcodes li {
750
+ padding: 0;
751
+ margin: 0;
752
+ line-height: 18px;
753
+ }
754
+
755
+ .wpforo-menu-shortcodes .wpforo-menu-shortcodes-head{
756
+ cursor: pointer;
757
+ opacity: 0.8;
758
+ }
759
+
760
+ .wpforo-menu-shortcodes .wpforo-menu-shortcodes-head:hover{
761
+ opacity: 1;
762
+ }
763
+
764
+ /* settings search input field autocomplate style */
765
+ /*the container must be positioned relative:*/
766
+ .autocomplete {
767
+ position: relative;
768
+ display: inline-block;
769
+ }
770
+
771
+ input {
772
+ border: 1px solid transparent;
773
+ background-color: #f1f1f1;
774
+ padding: 10px;
775
+ font-size: 16px;
776
+ }
777
+
778
+ input[type=text] {
779
+ background-color: #f1f1f1;
780
+ }
781
+
782
+ input[type=submit] {
783
+ background-color: DodgerBlue;
784
+ color: #fff;
785
+ cursor: pointer;
786
+ }
787
+
788
+ .autocomplete-items {
789
+ position: absolute;
790
+ border: 1px solid #d4d4d4;
791
+ border-bottom: none;
792
+ border-top: none;
793
+ top: 100%;
794
+ right: 0;
795
+ text-align: left;
796
+ height: 420px;
797
+ width: 100%;
798
+ overflow-x: hidden;
799
+ overflow-y: auto;
800
+ }
801
+
802
+ .autocomplete-items div {
803
+ padding: 10px;
804
+ cursor: pointer;
805
+ background-color: #fff;
806
+ border-bottom: 1px solid #d4d4d4;
807
+ }
808
+
809
+ /*when hovering an item:*/
810
+ .autocomplete-items div:hover {
811
+ background-color: #e9e9e9;
812
+ }
813
+
814
+ /*when navigating through the items using the arrow keys:*/
815
+ .autocomplete-active {
816
+ background-color: DodgerBlue !important;
817
+ color: #ffffff;
818
+ }
819
+
820
+ .autocomplete-active a{
821
+ color: #FFFFFF;
822
+ }
823
+
824
+ #wpf-forum-cover{box-sizing: border-box; width: 100%; height: 150px; border-radius: 10px; margin: 20px 0; border: 2px dashed #ccc; padding: 10px; display:flex; justify-content: center; align-items: center; background-position: center; background-size: cover; background-color: #fafafa;}
825
+
826
+
827
+ /* Extra RTL */
828
+ [dir=rtl] #wpf-dash .wpf-overview .wpf-stat-user .wpf-user-table td:nth-child(1) {text-align: right;}
829
+ [dir=rtl] #wpf-dash .wpf-overview .wpf-stat-user .wpf-user-table th:nth-child(1) {text-align: right;}
830
+ [dir=rtl] #wpf-setbox .wpf-setbox-body input[type="text"] {padding-right: 25px;}
831
+ [dir=rtl] #wpf-setbox .wpf-setbox-body .wpf-box{float: right;margin: 0 0 25px 1%;}
832
+ [dir=rtl] #wpf-setbox .wpf-setbox-body .wpf-setbar{width: 332px;}
833
+ [dir=rtl] #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-arrow img {-webkit-transform:rotate(180deg);-moz-transform: rotate(180deg);-ms-transform: rotate(180deg);-o-transform: rotate(180deg);transform: rotate(180deg);}
834
+ [dir=rtl] #wpf-setbox .wpf-setbox-body .wpf-box .wpf-box-foot .wpf-box-arrow{padding-right: 1px; padding-left: 20px;}
835
+ [dir=rtl] #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-doc {text-align: left;}
836
+
837
+ @media screen and (min-width:1601px){
838
+ #wpf-setbox .wpf-settings-home .wpf-box{width: 15.2%; margin: 0 1.2% 25px 0;}
839
+ }
840
+ @media screen and (max-width:1600px){
841
+ #wpf-setbox .wpf-settings-home .wpf-box{width: 19%; margin: 0 1% 25px 0;}
842
+ #wpf-setbox .wpf-setcon .wpf-opt-row .wpf-opt-input {min-width: 150px;}
843
+ }
844
+ @media screen and (max-width:1440px){
845
+ #wpf-setbox .wpf-settings-home .wpf-box{width: 23%; margin: 0 2% 25px 0;}
846
+ }
847
+ @media screen and (max-width:1280px){
848
+ #wpf-setbox .wpf-settings-home .wpf-box{width: 30%; margin: 0 3% 25px 0;}
849
+ #wpf-setbox .wpf-setbox-body .wpf-setcon{width: 100%; float: none; margin-bottom: 20px;}
850
+ #wpf-setbox .wpf-setbox-body .wpf-setbar{width: 100%; float: none;}
851
+ }
852
+ @media screen and (max-width:1024px){
853
+ #wpf-dash .wpf-overview{display: block;}
854
+ #wpf-dash .wpf-overview .wpf-stat-brief,
855
+ #wpf-dash .wpf-overview .wpf-stat-graph,
856
+ #wpf-dash .wpf-overview .wpf-stat-user,
857
+ #wpf-dash .wpf-overview .wpf-stat-subs{width: 100%!important;}
858
+ #wpf-dash .wpf-credits{display: block;}
859
+ #wpf-setbox .wpf-setbox-body input[type="text"],
860
+ #wpf-setbox .wpf-setbox-body input[type="url"]{width: 100%;}
861
+ #wpf-setbox .wpf-setcon .wpf-switch-field label {float: none;display: inline-block;min-width: 90%;padding: 2px 0;}
862
+ #wpf-setbox .wpf-setcon .wpf-color-wrap{width: 30% !important;}
863
+ }
864
+ @media screen and (max-width:800px){
865
+ .wpf-admin-wrap{padding-right: 10px;}
866
+ #wpf-setbox .wpf-settings-home .wpf-box {width: 47%;margin: 0 3% 25px 0;}
867
+ .wpf-board{flex-direction: column;}
868
+ .wpf-board-left{width: 100%}
869
+ .wpf-board-right{width: 100%; margin-left: 0;}
870
+ }
871
+ @media screen and (max-width:600px){
872
+ #wpf-setbox .wpf-setbox-body .wpf-section{display: block;}
873
+ #wpf-setbox .wpf-setbox-body input[type="text"] {width: 100%;display: block;margin-top: 10px;min-height: auto;}
874
+ #wpf-setbox .wpf-setbox-head .wpf-head-info {font-size: 14px;margin-top: 12px;}
875
+ }
876
+ @media screen and (max-width:480px){
877
+ #wpf-setbox .wpf-settings-home .wpf-box{width: 100%; margin: 0 0 25px 0;}
878
+ }
{wpf-admin → admin/assets}/css/deactivation-dialog.css RENAMED
File without changes
{wpf-admin → admin/assets}/css/index.html RENAMED
File without changes
{wpf-admin → admin/assets}/html/index.html RENAMED
File without changes
{wpf-admin → admin/assets}/html/privacy-policy-gdpr.html RENAMED
File without changes
{wpf-admin → admin/assets}/html/simple-forum-rules.html RENAMED
File without changes
admin/assets/js/backend.js ADDED
@@ -0,0 +1,498 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* global wpforo */
2
+
3
+ jQuery.ajaxSetup({
4
+ url: ajaxurl,
5
+ data:{
6
+ referer: window.location.origin + window.location.pathname
7
+ }
8
+ });
9
+
10
+ jQuery(document).ready(function ($) {
11
+ var wpforo_admin_wrap = $( '#wpf-admin-wrap' );
12
+
13
+ $(document).on('click', '#wpforo-admin-notice-recaptcha .notice-dismiss', function () {
14
+ var wrap = $(this).closest('#wpforo-admin-notice-recaptcha')
15
+ $.ajax({
16
+ type: 'POST', url: ajaxurl, data: {
17
+ backend: 1, action: 'wpforo_dissmiss_recaptcha_note'
18
+ }
19
+ }).done(function (response) {
20
+ if (parseInt(response)) {
21
+ wrap.remove()
22
+ }
23
+ })
24
+ })
25
+
26
+ var fas = $('[name$="[icon]"]')
27
+ fas.each(function () {
28
+ var fa = $(this)
29
+ fa.iconpicker({
30
+ placement: 'top',
31
+ selectedCustomClass: 'wpf-fa-ico-primary-bg',
32
+ component: '.wpf-fa-ico-preview',
33
+ collision: true,
34
+ animation: true
35
+ })
36
+ var w = document.createElement('span')
37
+ $(w).addClass('wpf-input-group-addon')
38
+ var i = document.createElement('i')
39
+ $(i).addClass(fa.val())
40
+ w.append(i)
41
+ fa.after(w)
42
+ fa.on('iconpickerUpdated input propertychange', function () {
43
+ $(i).removeClass().addClass($(this).val())
44
+ })
45
+ });
46
+
47
+ wpforo_scrollto_setting_field(window.location);
48
+ window.onhashchange = function(){
49
+ wpforo_scrollto_setting_field(window.location);
50
+ };
51
+
52
+ var search_field = document.getElementById("wpf-opt-search-field");
53
+ if( search_field ) autocomplete( search_field, wpforo.settings.info );
54
+
55
+ /* -- #################################################### -- */
56
+ /* image adjust and upload from frontend */
57
+
58
+ function processfile( file, max_width, max_height, quality, callback ) {
59
+ if( !( /image/i ).test( file.type ) ) {
60
+ alert( "File "+ file.name +" is not an image." );
61
+ return false;
62
+ }
63
+
64
+ // read the files
65
+ var reader = new FileReader();
66
+ reader.readAsArrayBuffer(file);
67
+
68
+ reader.onload = function (event) {
69
+ // blob stuff
70
+ var blob = new Blob([event.target.result]); // create blob...
71
+ window.URL = window.URL || window.webkitURL;
72
+ var blobURL = window.URL.createObjectURL(blob); // and get its URL
73
+
74
+ // helper Image object
75
+ var image = new Image();
76
+ image.src = blobURL;
77
+ //preview.appendChild(image); // preview commented out, I am using the canvas instead
78
+ image.onload = function() {
79
+ // have to wait till it's loaded
80
+ var resized = resizeMe(image, max_width, max_height, quality); // send it to canvas
81
+ callback( resized );
82
+ }
83
+ };
84
+ }
85
+
86
+ // === RESIZE ====
87
+ function resizeMe( img, max_width, max_height, quality ) {
88
+ var canvas = document.createElement('canvas');
89
+ var width = img.width;
90
+ var height = img.height;
91
+
92
+ // calculate the width and height, constraining the proportions
93
+ if(width > height) {
94
+ if (width > max_width) {
95
+ //height *= max_width / width;
96
+ height = Math.round(height *= max_width / width);
97
+ width = max_width;
98
+ }
99
+ } else {
100
+ if (height > max_height) {
101
+ //width *= max_height / height;
102
+ width = Math.round(width *= max_height / height);
103
+ height = max_height;
104
+ }
105
+ }
106
+
107
+ // resize the canvas and draw the image data into it
108
+ canvas.width = width;
109
+ canvas.height = height;
110
+ var ctx = canvas.getContext("2d");
111
+ ctx.drawImage(img, 0, 0, width, height);
112
+
113
+ return canvas.toDataURL("image/jpeg", quality); // get the data from canvas as 70% JPG (can be also PNG, etc.)
114
+ }
115
+
116
+ function OpenFileDialog (accept, callback) {
117
+ // this function must be called from a user
118
+ // activation event (ie an onclick event)
119
+
120
+ // Create an input element
121
+ var inputElement = document.createElement("input");
122
+
123
+ // Set its type to file
124
+ inputElement.type = "file";
125
+
126
+ // Set accept to the file types you want the user to select.
127
+ // Include both the file extension and the mime type
128
+ inputElement.accept = accept;
129
+
130
+ // set onchange event to call callback when user has selected file
131
+ inputElement.addEventListener("change", callback)
132
+
133
+ // dispatch a click event to open the file dialog
134
+ inputElement.dispatchEvent(new MouseEvent("click"));
135
+ }
136
+
137
+ wpforo_admin_wrap.on('click', 'label', {}, function(){
138
+ $( '.wpf-edit-cover', $( this ).closest( '.wpf-opt-row-cover' ) ).trigger( 'click' );
139
+ });
140
+
141
+ // ***** -- cover image change -- ****** //
142
+ wpforo_admin_wrap.on( 'click', '.wpf-opt-row-cover .wpf-edit-cover:not(.wpf-processing)', {}, function(){
143
+ if ( !( window.File && window.FileReader && window.FileList && window.Blob ) ) {
144
+ alert('The File APIs are not fully supported in this browser.');
145
+ return false;
146
+ }
147
+ var $this = $( this );
148
+ OpenFileDialog('image/*', function(){
149
+ if( this.files.length ) {
150
+ $this.addClass( 'wpf-processing' );
151
+ $( '#wpf-admin-loading-extrawrap' ).show();
152
+ processfile(this.files[0], 1120, 460, 0.7, function (imageblob) {
153
+ $.ajax({
154
+ type: 'POST',
155
+ data: {
156
+ image_blob: imageblob,
157
+ action: 'wpforo_profiles_default_cover_upload'
158
+ }
159
+ }).done(function(){
160
+ $this.closest('.wpf-cover').css('background-image', 'url(\'' + imageblob + '\')');
161
+ }).always(function(){
162
+ $this.removeClass('wpf-processing');
163
+ $( '#wpf-admin-loading-extrawrap' ).hide();
164
+ });
165
+ })
166
+ }
167
+ });
168
+ });
169
+
170
+ $( 'body' ).on('click', '.wpforo-menu-shortcodes .wpforo-menu-shortcodes-head', {}, function(){
171
+ var $this = $( this );
172
+ var wrap = $this.closest( '.wpforo-menu-shortcodes' );
173
+ var body = $( '.wpforo-menu-shortcodes-body', wrap );
174
+ body.toggle();
175
+ });
176
+ });
177
+
178
+ function get_parentid (arr, depth) {
179
+ for (var i = arr.length - 1; i >= 0; i--) {
180
+ if (arr[i]['depth'] == depth) return arr[i]['forumid']
181
+ }
182
+ }
183
+
184
+ function get_forums_hierarchy () {
185
+
186
+ var ul_content = document.getElementById('menu-to-edit')
187
+ var lis = ul_content.getElementsByTagName('LI')
188
+
189
+ var forums_hierarchy_arr = new Array()
190
+
191
+ for (var i = 0; i < lis.length; i++) {
192
+ forums_hierarchy_arr[i] = new Array()
193
+ forums_hierarchy_arr[i]['forumid'] = lis[i].id.replace('menu-item-', '')
194
+ var depth = lis[i].getAttribute('class').replace('menu-item menu-item-depth-', '')
195
+
196
+ forums_hierarchy_arr[i]['depth'] = depth
197
+
198
+ if (depth == 0) {
199
+ forums_hierarchy_arr[i]['parentid'] = 0
200
+ } else {
201
+ var previous_depth = depth - 1
202
+ forums_hierarchy_arr[i]['parentid'] = get_parentid(forums_hierarchy_arr, previous_depth)
203
+ }
204
+
205
+ forums_hierarchy_arr[i]['order'] = i + 1
206
+
207
+ var h_id = 'forumid-' + forums_hierarchy_arr[i]['forumid']
208
+ var h_parentid = 'parentid-' + forums_hierarchy_arr[i]['forumid']
209
+ var h_order = 'order-' + forums_hierarchy_arr[i]['forumid']
210
+
211
+ document.getElementById(h_id).value = forums_hierarchy_arr[i]['forumid']
212
+ document.getElementById(h_parentid).value = forums_hierarchy_arr[i]['parentid']
213
+ document.getElementById(h_order).value = forums_hierarchy_arr[i]['order']
214
+
215
+ }
216
+
217
+ document.getElementById('forum-hierarchy').submit()
218
+
219
+ }
220
+
221
+ function mode_changer (v) {
222
+ if (v) {
223
+ document.getElementById('forum_submit').value = wpforo_admin.phrases.move
224
+ document.getElementById('forum_select').disabled = false
225
+ } else {
226
+ document.getElementById('forum_submit').value = wpforo_admin.phrases.delete
227
+ document.getElementById('forum_select').disabled = true
228
+ }
229
+ }
230
+
231
+ function select_all () {
232
+ var sel_all = document.getElementById('cb-select-all-1')
233
+ var list = document.getElementById('the-list').getElementsByTagName('INPUT')
234
+ for (var i = 0; i < list.length; i++) document.getElementById(list[i].id).checked = !!sel_all.checked
235
+ }
236
+
237
+ function costum_or_inherit() {
238
+ var chack = document.getElementById('custom')
239
+ document.getElementById('permis').disabled = !!chack.checked;
240
+ }
241
+
242
+ function mode_changer_ug(v) {
243
+ document.getElementById('ug_select').disabled = !v;
244
+ }
245
+
246
+ function wpforo_scrollto_setting_field( location ){
247
+ if( location.hash ){
248
+ var scroll_to;
249
+ var exreg = new RegExp('&wpf_tab=([^=&?#\\s]+)', 'i');
250
+ var match = location.href.match(exreg);
251
+ if( match ){
252
+ var hash = location.hash.toString().slice(1);
253
+ var name = match[1] + '\\['+ hash +'\\]';
254
+ var wrap_row = jQuery( '[data-wpf-opt="'+ hash +'"]' );
255
+ if( wrap_row.length ){
256
+ scroll_to = wrap_row.offset().top - 150;
257
+ wpForoScrollTo(scroll_to, function () {
258
+ wrap_row.addClass("wpf-selected");
259
+ if( ! jQuery( '#wpf-opt-search-field' ).is(':focus') ){
260
+ var field = jQuery( '[name^='+ name +']', wrap_row );
261
+ field.trigger('focus');
262
+ field.trigger('select');
263
+ }
264
+ setTimeout(function () {
265
+ wrap_row.removeClass("wpf-selected");
266
+ }, 3000);
267
+ });
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ function wpforo_admin_tinymce_setup(editor){
274
+ editor.on('init', function(e) {
275
+ wpforo_scrollto_setting_field(window.location);
276
+ });
277
+ }
278
+
279
+ function wpForoScrollTo(top, callback, element) {
280
+ var html = element ? element : document.documentElement;
281
+ var current = html.scrollTop;
282
+ var delta = top - current;
283
+ var finish = function () {
284
+ html.scrollTop = top;
285
+ if (callback) {
286
+ callback();
287
+ }
288
+ };
289
+ if (!window.performance.now || delta === 0) {
290
+ finish();
291
+ return;
292
+ }
293
+ var transition = wpForoEaseOutQuad;
294
+ var max = 300;
295
+ if (delta < -max) {
296
+ current = top + max;
297
+ delta = -max;
298
+ } else if (delta > max) {
299
+ current = top - max;
300
+ delta = max;
301
+ } else {
302
+ transition = wpForoEaseInOutQuad;
303
+ }
304
+ var duration = 150;
305
+ var interval = 7;
306
+ var time = window.performance.now();
307
+ var animate = function () {
308
+ var now = window.performance.now();
309
+ if (now >= time + duration) {
310
+ finish();
311
+ return;
312
+ }
313
+ var dt = (now - time) / duration;
314
+ html.scrollTop = Math.round(current + delta * transition(dt));
315
+ setTimeout(animate, interval);
316
+ };
317
+ setTimeout(animate, interval);
318
+ }
319
+
320
+ function wpForoEaseOutQuad(t) {
321
+ return t * t;
322
+ }
323
+
324
+ function wpForoEaseInOutQuad(t) {
325
+ return (t < 0.5) ? (2 * t * t) : ((4 - 2 * t) * t - 1);
326
+ }
327
+
328
+
329
+ // ----------- autocomplate settings search ----------------- //
330
+
331
+ function autocomplete(inp, arr) {
332
+ var m;
333
+ var page = '';
334
+ var page_regexp = new RegExp('[?&]page=([^=&?#\\s]+)', 'i');
335
+ m = window.location.href.match(page_regexp);
336
+ if( m ) page = m[1];
337
+
338
+ var wpf_tab = '';
339
+ var wpf_tab_regexp = new RegExp('[?&]wpf_tab=([^=&?#\\s]+)', 'i');
340
+ m = window.location.href.match(wpf_tab_regexp);
341
+ if( m ) wpf_tab = m[1];
342
+
343
+ /*the autocomplete function takes two arguments,
344
+ the text field element and an array of possible autocompleted values:*/
345
+ var currentFocus;
346
+ /*execute a function when someone writes in the text field:*/
347
+ inp.addEventListener("input", function() {
348
+ var a, b, val = this.value;
349
+ /*close any already open lists of autocompleted values*/
350
+ closeAllLists();
351
+ if (!val) { return false;}
352
+ currentFocus = -1;
353
+ /*create a DIV element that will contain the items (values):*/
354
+ a = document.createElement("DIV");
355
+ a.setAttribute("id", this.id + "-autocomplete-list");
356
+ a.setAttribute("class", "autocomplete-items");
357
+ /*append the DIV element as a child of the autocomplete container:*/
358
+ this.parentNode.appendChild(a);
359
+ /*for each item in the array...*/
360
+
361
+ var pattern = val.trim().split(/\s+/).filter(function(value){ return value.length > 2 }).join('|');
362
+ if( !pattern ) return;
363
+ var exreg = new RegExp( pattern, 'iug' );
364
+ var relevancy = [];
365
+ for( var i in arr.core ){
366
+ var ss,st,sto,sd,sdo;
367
+ var s = arr.core[i];
368
+ if( (ss = i.match( exreg ))
369
+ || (s['title'] && (st = s['title'].match( exreg )))
370
+ || (s['title_original'] && (sto = s['title_original'].match( exreg )))
371
+ || (s['description'] && (sd = s['description'].match( exreg )))
372
+ || (s['description_original'] && (sdo = s['description_original'].match( exreg ))) ){
373
+ relevancy.push( {
374
+ setting : s,
375
+ option : {},
376
+ label : s['title'],
377
+ wpf_tab : i,
378
+ optname : '',
379
+ points : (ss ? ss.length * 3 : 0)
380
+ + (st ? st.length * 2 : 0)
381
+ + (sto ? sto.length * 2 : 0)
382
+ + (sd ? sd.length : 0)
383
+ + (sdo ? sdo.length : 0)
384
+ });
385
+ }
386
+
387
+ var os,ol,olo,od,odo;
388
+ for (var k in s['options']) {
389
+ var o = s['options'][k];
390
+ if( ( os = k.match( exreg ) )
391
+ || (o['label'] && (ol = o['label'].match( exreg )))
392
+ || (o['label_original'] && (olo = o['label_original'].match( exreg )))
393
+ || (o['description'] && (od = o['description'].match( exreg )))
394
+ || (o['description_original'] && (odo = o['description_original'].match( exreg ))) ){
395
+
396
+ relevancy.push( {
397
+ label : o['label'],
398
+ setting : s,
399
+ option : o,
400
+ wpf_tab : i,
401
+ optname : k,
402
+ points : (os ? os.length * 4 : 0)
403
+ + (ol ? ol.length * 3 : 0)
404
+ + (olo ? olo.length * 3 : 0)
405
+ + (od ? od.length * 2 : 0)
406
+ + (odo ? odo.length * 2 : 0)
407
+ });
408
+ }
409
+ }
410
+
411
+ }
412
+
413
+ relevancy.sort( function(a,b){ return b.points - a.points } );
414
+ relevancy.forEach( function(value){
415
+ var _page = value.setting.base && wpforo.board.has_more_boards ? 'wpforo-base-settings' : page;
416
+ if( !value.setting.base && _page === 'wpforo-base-settings' ) _page = 'wpforo-settings';
417
+ b = document.createElement("DIV");
418
+ if( value.optname ){
419
+ b.innerHTML = `<strong>OPTION:</strong>&nbsp;<a href="admin.php?page=${ _page }&wpf_tab=${ value.wpf_tab }#${ value.optname }">${ value.label } #${ value.optname }</a>`;
420
+ }else{
421
+ b.innerHTML = `<strong>SETTINGS:</strong>&nbsp;<a href="admin.php?page=${ _page }&wpf_tab=${ value.wpf_tab }">${ value.label }</a>`;
422
+ }
423
+ a.appendChild(b);
424
+ });
425
+
426
+ var scrto = relevancy.find( function( value ){ return value.wpf_tab === wpf_tab && value.optname });
427
+ if( scrto && window.location.hash !== '#' + scrto.optname ){
428
+ closeAllLists();
429
+ window.location.hash = '';
430
+ window.location.hash = scrto.optname;
431
+ }
432
+
433
+ });
434
+ /*execute a function presses a key on the keyboard:*/
435
+ inp.addEventListener("keydown", function(e) {
436
+ var x = document.getElementById(this.id + "-autocomplete-list");
437
+ if (x) x = x.getElementsByTagName("div");
438
+ if (e.code === 'ArrowDown') {
439
+ /*If the arrow DOWN key is pressed,
440
+ increase the currentFocus variable:*/
441
+ currentFocus++;
442
+ /*and make the current item more visible:*/
443
+ addActive(x);
444
+ } else if (e.code === 'ArrowUp') { //up
445
+ /*If the arrow UP key is pressed,
446
+ decrease the currentFocus variable:*/
447
+ currentFocus--;
448
+ /*and make the current item more visible:*/
449
+ addActive(x);
450
+ }else if (e.code === 'Home') {
451
+ currentFocus = 0;
452
+ addActive(x);
453
+ }else if (e.code === 'End') {
454
+ currentFocus = x.length - 1;
455
+ addActive(x);
456
+ } else if (e.code === 'Enter') {
457
+ /*If the ENTER key is pressed, prevent the form from being submitted,*/
458
+ e.preventDefault();
459
+ if (currentFocus > -1) {
460
+ /*and simulate a click on the "active" item:*/
461
+ if (x) x[currentFocus].querySelector('a').click();
462
+ }
463
+ }
464
+ });
465
+ function addActive(x) {
466
+ /*a function to classify an item as "active":*/
467
+ if (!x) return false;
468
+ /*start by removing the "active" class on all items:*/
469
+ removeActive(x);
470
+ if (currentFocus >= x.length) currentFocus = 0;
471
+ if (currentFocus < 0) currentFocus = (x.length - 1);
472
+ var autocomplete_items = document.querySelector( '.autocomplete-items' );
473
+ /*add class "autocomplete-active":*/
474
+ x[currentFocus].classList.add("autocomplete-active");
475
+ var delta = x[currentFocus].offsetTop - autocomplete_items.scrollTop;
476
+ if( delta < 0 || delta > 300 ){
477
+ wpForoScrollTo( x[currentFocus].offsetTop - 117, undefined, document.querySelector( '.autocomplete-items' ) );
478
+ }
479
+ }
480
+ function removeActive(x) {
481
+ /*a function to remove the "active" class from all autocomplete items:*/
482
+ for (var i = 0; i < x.length; i++) {
483
+ x[i].classList.remove("autocomplete-active");
484
+ }
485
+ }
486
+ function closeAllLists(elmnt) {
487
+ /*close all autocomplete lists in the document,
488
+ except the one passed as an argument:*/
489
+ var x = document.getElementsByClassName("autocomplete-items");
490
+ for (var i = 0; i < x.length; i++) {
491
+ if (elmnt != x[i] && elmnt != inp) {
492
+ x[i].parentNode.removeChild(x[i]);
493
+ }
494
+ }
495
+ }
496
+ /*execute a function when someone clicks in the document:*/
497
+ document.addEventListener("click", function (e) { closeAllLists(e.target); });
498
+ }
{wpf-admin/includes → admin/assets/js/contenthover}/index.html RENAMED
File without changes
{wpf-admin → admin/assets}/js/contenthover/jquery.contenthover.min.js RENAMED
File without changes
{wpf-admin → admin/assets}/js/deactivation-dialog.js RENAMED
@@ -47,7 +47,8 @@ jQuery(document).ready(function ($) {
47
  var reasonDesc = $('textarea[name="wpforo_deactivation_reason_desc"]', parentItem);
48
  var reasonFeedback = $('input[name="wpforo_deactivation_feedback"]', parentItem);
49
  var reasonFeedbackEmail = $('input[name="wpforo_deactivation_feedback_email"]', parentItem);
50
- var reasonFeedbackEmailVal = reasonFeedbackEmail.val().trim();
 
51
  var isValid = true;
52
 
53
  if (reasonDesc.length && reasonDesc.is(':visible')) {
@@ -151,4 +152,4 @@ jQuery(document).ready(function ($) {
151
  return re.test(email);
152
  }
153
 
154
- });
47
  var reasonDesc = $('textarea[name="wpforo_deactivation_reason_desc"]', parentItem);
48
  var reasonFeedback = $('input[name="wpforo_deactivation_feedback"]', parentItem);
49
  var reasonFeedbackEmail = $('input[name="wpforo_deactivation_feedback_email"]', parentItem);
50
+ var reasonFeedbackEmailVal = '';
51
+ if( reasonFeedbackEmail.length ) reasonFeedbackEmailVal = reasonFeedbackEmail.val().trim();
52
  var isValid = true;
53
 
54
  if (reasonDesc.length && reasonDesc.is(':visible')) {
152
  return re.test(email);
153
  }
154
 
155
+ });
{wpf-admin → admin/assets/js}/index.html RENAMED
File without changes
admin/assets/third-party/iconpicker/fontawesome-iconpicker.js ADDED
@@ -0,0 +1,1038 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function(a) {
2
+ if (typeof define === "function" && define.amd) {
3
+ define([ "jquery" ], a);
4
+ } else {
5
+ a(jQuery);
6
+ }
7
+ })(function(a) {
8
+ a.ui = a.ui || {};
9
+ var b = a.ui.version = "1.12.1";
10
+ (function() {
11
+ var b, c = Math.max, d = Math.abs, e = /left|center|right/, f = /top|center|bottom/, g = /[\+\-]\d+(\.[\d]+)?%?/, h = /^\w+/, i = /%$/, j = a.fn.pos;
12
+ function k(a, b, c) {
13
+ return [ parseFloat(a[0]) * (i.test(a[0]) ? b / 100 : 1), parseFloat(a[1]) * (i.test(a[1]) ? c / 100 : 1) ];
14
+ }
15
+ function l(b, c) {
16
+ return parseInt(a.css(b, c), 10) || 0;
17
+ }
18
+ function m(b) {
19
+ var c = b[0];
20
+ if (c.nodeType === 9) {
21
+ return {
22
+ width: b.width(),
23
+ height: b.height(),
24
+ offset: {
25
+ top: 0,
26
+ left: 0
27
+ }
28
+ };
29
+ }
30
+ if (a.isWindow(c)) {
31
+ return {
32
+ width: b.width(),
33
+ height: b.height(),
34
+ offset: {
35
+ top: b.scrollTop(),
36
+ left: b.scrollLeft()
37
+ }
38
+ };
39
+ }
40
+ if (c.preventDefault) {
41
+ return {
42
+ width: 0,
43
+ height: 0,
44
+ offset: {
45
+ top: c.pageY,
46
+ left: c.pageX
47
+ }
48
+ };
49
+ }
50
+ return {
51
+ width: b.outerWidth(),
52
+ height: b.outerHeight(),
53
+ offset: b.offset()
54
+ };
55
+ }
56
+ a.pos = {
57
+ scrollbarWidth: function() {
58
+ if (b !== undefined) {
59
+ return b;
60
+ }
61
+ var c, d, e = a("<div " + "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" + "<div style='height:100px;width:auto;'></div></div>"), f = e.children()[0];
62
+ a("body").append(e);
63
+ c = f.offsetWidth;
64
+ e.css("overflow", "scroll");
65
+ d = f.offsetWidth;
66
+ if (c === d) {
67
+ d = e[0].clientWidth;
68
+ }
69
+ e.remove();
70
+ return b = c - d;
71
+ },
72
+ getScrollInfo: function(b) {
73
+ var c = b.isWindow || b.isDocument ? "" : b.element.css("overflow-x"), d = b.isWindow || b.isDocument ? "" : b.element.css("overflow-y"), e = c === "scroll" || c === "auto" && b.width < b.element[0].scrollWidth, f = d === "scroll" || d === "auto" && b.height < b.element[0].scrollHeight;
74
+ return {
75
+ width: f ? a.pos.scrollbarWidth() : 0,
76
+ height: e ? a.pos.scrollbarWidth() : 0
77
+ };
78
+ },
79
+ getWithinInfo: function(b) {
80
+ var c = a(b || window), d = a.isWindow(c[0]), e = !!c[0] && c[0].nodeType === 9, f = !d && !e;
81
+ return {
82
+ element: c,
83
+ isWindow: d,
84
+ isDocument: e,
85
+ offset: f ? a(b).offset() : {
86
+ left: 0,
87
+ top: 0
88
+ },
89
+ scrollLeft: c.scrollLeft(),
90
+ scrollTop: c.scrollTop(),
91
+ width: c.outerWidth(),
92
+ height: c.outerHeight()
93
+ };
94
+ }
95
+ };
96
+ a.fn.pos = function(b) {
97
+ if (!b || !b.of) {
98
+ return j.apply(this, arguments);
99
+ }
100
+ b = a.extend({}, b);
101
+ var i, n, o, p, q, r, s = a(b.of), t = a.pos.getWithinInfo(b.within), u = a.pos.getScrollInfo(t), v = (b.collision || "flip").split(" "), w = {};
102
+ r = m(s);
103
+ if (s[0].preventDefault) {
104
+ b.at = "left top";
105
+ }
106
+ n = r.width;
107
+ o = r.height;
108
+ p = r.offset;
109
+ q = a.extend({}, p);
110
+ a.each([ "my", "at" ], function() {
111
+ var a = (b[this] || "").split(" "), c, d;
112
+ if (a.length === 1) {
113
+ a = e.test(a[0]) ? a.concat([ "center" ]) : f.test(a[0]) ? [ "center" ].concat(a) : [ "center", "center" ];
114
+ }
115
+ a[0] = e.test(a[0]) ? a[0] : "center";
116
+ a[1] = f.test(a[1]) ? a[1] : "center";
117
+ c = g.exec(a[0]);
118
+ d = g.exec(a[1]);
119
+ w[this] = [ c ? c[0] : 0, d ? d[0] : 0 ];
120
+ b[this] = [ h.exec(a[0])[0], h.exec(a[1])[0] ];
121
+ });
122
+ if (v.length === 1) {
123
+ v[1] = v[0];
124
+ }
125
+ if (b.at[0] === "right") {
126
+ q.left += n;
127
+ } else if (b.at[0] === "center") {
128
+ q.left += n / 2;
129
+ }
130
+ if (b.at[1] === "bottom") {
131
+ q.top += o;
132
+ } else if (b.at[1] === "center") {
133
+ q.top += o / 2;
134
+ }
135
+ i = k(w.at, n, o);
136
+ q.left += i[0];
137
+ q.top += i[1];
138
+ return this.each(function() {
139
+ var e, f, g = a(this), h = g.outerWidth(), j = g.outerHeight(), m = l(this, "marginLeft"), r = l(this, "marginTop"), x = h + m + l(this, "marginRight") + u.width, y = j + r + l(this, "marginBottom") + u.height, z = a.extend({}, q), A = k(w.my, g.outerWidth(), g.outerHeight());
140
+ if (b.my[0] === "right") {
141
+ z.left -= h;
142
+ } else if (b.my[0] === "center") {
143
+ z.left -= h / 2;
144
+ }
145
+ if (b.my[1] === "bottom") {
146
+ z.top -= j;
147
+ } else if (b.my[1] === "center") {
148
+ z.top -= j / 2;
149
+ }
150
+ z.left += A[0];
151
+ z.top += A[1];
152
+ e = {
153
+ marginLeft: m,
154
+ marginTop: r
155
+ };
156
+ a.each([ "left", "top" ], function(c, d) {
157
+ if (a.ui.pos[v[c]]) {
158
+ a.ui.pos[v[c]][d](z, {
159
+ targetWidth: n,
160
+ targetHeight: o,
161
+ elemWidth: h,
162
+ elemHeight: j,
163
+ collisionPosition: e,
164
+ collisionWidth: x,
165
+ collisionHeight: y,
166
+ offset: [ i[0] + A[0], i[1] + A[1] ],
167
+ my: b.my,
168
+ at: b.at,
169
+ within: t,
170
+ elem: g
171
+ });
172
+ }
173
+ });
174
+ if (b.using) {
175
+ f = function(a) {
176
+ var e = p.left - z.left, f = e + n - h, i = p.top - z.top, k = i + o - j, l = {
177
+ target: {
178
+ element: s,
179
+ left: p.left,
180
+ top: p.top,
181
+ width: n,
182
+ height: o
183
+ },
184
+ element: {
185
+ element: g,
186
+ left: z.left,
187
+ top: z.top,
188
+ width: h,
189
+ height: j
190
+ },
191
+ horizontal: f < 0 ? "left" : e > 0 ? "right" : "center",
192
+ vertical: k < 0 ? "top" : i > 0 ? "bottom" : "middle"
193
+ };
194
+ if (n < h && d(e + f) < n) {
195
+ l.horizontal = "center";
196
+ }
197
+ if (o < j && d(i + k) < o) {
198
+ l.vertical = "middle";
199
+ }
200
+ if (c(d(e), d(f)) > c(d(i), d(k))) {
201
+ l.important = "horizontal";
202
+ } else {
203
+ l.important = "vertical";
204
+ }
205
+ b.using.call(this, a, l);
206
+ };
207
+ }
208
+ g.offset(a.extend(z, {
209
+ using: f
210
+ }));
211
+ });
212
+ };
213
+ a.ui.pos = {
214
+ _trigger: function(a, b, c, d) {
215
+ if (b.elem) {
216
+ b.elem.trigger({
217
+ type: c,
218
+ position: a,
219
+ positionData: b,
220
+ triggered: d
221
+ });
222
+ }
223
+ },
224
+ fit: {
225
+ left: function(b, d) {
226
+ a.ui.pos._trigger(b, d, "posCollide", "fitLeft");
227
+ var e = d.within, f = e.isWindow ? e.scrollLeft : e.offset.left, g = e.width, h = b.left - d.collisionPosition.marginLeft, i = f - h, j = h + d.collisionWidth - g - f, k;
228
+ if (d.collisionWidth > g) {
229
+ if (i > 0 && j <= 0) {
230
+ k = b.left + i + d.collisionWidth - g - f;
231
+ b.left += i - k;
232
+ } else if (j > 0 && i <= 0) {
233
+ b.left = f;
234
+ } else {
235
+ if (i > j) {
236
+ b.left = f + g - d.collisionWidth;
237
+ } else {
238
+ b.left = f;
239
+ }
240
+ }
241
+ } else if (i > 0) {
242
+ b.left += i;
243
+ } else if (j > 0) {
244
+ b.left -= j;
245
+ } else {
246
+ b.left = c(b.left - h, b.left);
247
+ }
248
+ a.ui.pos._trigger(b, d, "posCollided", "fitLeft");
249
+ },
250
+ top: function(b, d) {
251
+ a.ui.pos._trigger(b, d, "posCollide", "fitTop");
252
+ var e = d.within, f = e.isWindow ? e.scrollTop : e.offset.top, g = d.within.height, h = b.top - d.collisionPosition.marginTop, i = f - h, j = h + d.collisionHeight - g - f, k;
253
+ if (d.collisionHeight > g) {
254
+ if (i > 0 && j <= 0) {
255
+ k = b.top + i + d.collisionHeight - g - f;
256
+ b.top += i - k;
257
+ } else if (j > 0 && i <= 0) {
258
+ b.top = f;
259
+ } else {
260
+ if (i > j) {
261
+ b.top = f + g - d.collisionHeight;
262
+ } else {
263
+ b.top = f;
264
+ }
265
+ }
266
+ } else if (i > 0) {
267
+ b.top += i;
268
+ } else if (j > 0) {
269
+ b.top -= j;
270
+ } else {
271
+ b.top = c(b.top - h, b.top);
272
+ }
273
+ a.ui.pos._trigger(b, d, "posCollided", "fitTop");
274
+ }
275
+ },
276
+ flip: {
277
+ left: function(b, c) {
278
+ a.ui.pos._trigger(b, c, "posCollide", "flipLeft");
279
+ var e = c.within, f = e.offset.left + e.scrollLeft, g = e.width, h = e.isWindow ? e.scrollLeft : e.offset.left, i = b.left - c.collisionPosition.marginLeft, j = i - h, k = i + c.collisionWidth - g - h, l = c.my[0] === "left" ? -c.elemWidth : c.my[0] === "right" ? c.elemWidth : 0, m = c.at[0] === "left" ? c.targetWidth : c.at[0] === "right" ? -c.targetWidth : 0, n = -2 * c.offset[0], o, p;
280
+ if (j < 0) {
281
+ o = b.left + l + m + n + c.collisionWidth - g - f;
282
+ if (o < 0 || o < d(j)) {
283
+ b.left += l + m + n;
284
+ }
285
+ } else if (k > 0) {
286
+ p = b.left - c.collisionPosition.marginLeft + l + m + n - h;
287
+ if (p > 0 || d(p) < k) {
288
+ b.left += l + m + n;
289
+ }
290
+ }
291
+ a.ui.pos._trigger(b, c, "posCollided", "flipLeft");
292
+ },
293
+ top: function(b, c) {
294
+ a.ui.pos._trigger(b, c, "posCollide", "flipTop");
295
+ var e = c.within, f = e.offset.top + e.scrollTop, g = e.height, h = e.isWindow ? e.scrollTop : e.offset.top, i = b.top - c.collisionPosition.marginTop, j = i - h, k = i + c.collisionHeight - g - h, l = c.my[1] === "top", m = l ? -c.elemHeight : c.my[1] === "bottom" ? c.elemHeight : 0, n = c.at[1] === "top" ? c.targetHeight : c.at[1] === "bottom" ? -c.targetHeight : 0, o = -2 * c.offset[1], p, q;
296
+ if (j < 0) {
297
+ q = b.top + m + n + o + c.collisionHeight - g - f;
298
+ if (q < 0 || q < d(j)) {
299
+ b.top += m + n + o;
300
+ }
301
+ } else if (k > 0) {
302
+ p = b.top - c.collisionPosition.marginTop + m + n + o - h;
303
+ if (p > 0 || d(p) < k) {
304
+ b.top += m + n + o;
305
+ }
306
+ }
307
+ a.ui.pos._trigger(b, c, "posCollided", "flipTop");
308
+ }
309
+ },
310
+ flipfit: {
311
+ left: function() {
312
+ a.ui.pos.flip.left.apply(this, arguments);
313
+ a.ui.pos.fit.left.apply(this, arguments);
314
+ },
315
+ top: function() {
316
+ a.ui.pos.flip.top.apply(this, arguments);
317
+ a.ui.pos.fit.top.apply(this, arguments);
318
+ }
319
+ }
320
+ };
321
+ (function() {
322
+ var b, c, d, e, f, g = document.getElementsByTagName("body")[0], h = document.createElement("div");
323
+ b = document.createElement(g ? "div" : "body");
324
+ d = {
325
+ visibility: "hidden",
326
+ width: 0,
327
+ height: 0,
328
+ border: 0,
329
+ margin: 0,
330
+ background: "none"
331
+ };
332
+ if (g) {
333
+ a.extend(d, {
334
+ position: "absolute",
335
+ left: "-1000px",
336
+ top: "-1000px"
337
+ });
338
+ }
339
+ for (f in d) {
340
+ b.style[f] = d[f];
341
+ }
342
+ b.appendChild(h);
343
+ c = g || document.documentElement;
344
+ c.insertBefore(b, c.firstChild);
345
+ h.style.cssText = "position: absolute; left: 10.7432222px;";
346
+ e = a(h).offset().left;
347
+ a.support.offsetFractions = e > 10 && e < 11;
348
+ b.innerHTML = "";
349
+ c.removeChild(b);
350
+ })();
351
+ })();
352
+ var c = a.ui.position;
353
+ });
354
+
355
+ (function(a) {
356
+ "use strict";
357
+ if (typeof define === "function" && define.amd) {
358
+ define([ "jquery" ], a);
359
+ } else if (window.jQuery && !window.jQuery.fn.iconpicker) {
360
+ a(window.jQuery);
361
+ }
362
+ })(function(a) {
363
+ "use strict";
364
+ var b = {
365
+ isEmpty: function(a) {
366
+ return a === false || a === "" || a === null || a === undefined;
367
+ },
368
+ isEmptyObject: function(a) {
369
+ return this.isEmpty(a) === true || a.length === 0;
370
+ },
371
+ isElement: function(b) {
372
+ return a(b).length > 0;
373
+ },
374
+ isString: function(a) {
375
+ return typeof a === "string" || a instanceof String;
376
+ },
377
+ isArray: function(b) {
378
+ return a.isArray(b);
379
+ },
380
+ inArray: function(b, c) {
381
+ return a.inArray(b, c) !== -1;
382
+ },
383
+ throwError: function(a) {
384
+ throw "Font Awesome Icon Picker Exception: " + a;
385
+ }
386
+ };
387
+ var c = function(d, e) {
388
+ this._id = c._idCounter++;
389
+ this.element = a(d).addClass("iconpicker-element");
390
+ this._trigger("iconpickerCreate");
391
+ this.options = a.extend({}, c.defaultOptions, this.element.data(), e);
392
+ this.options.templates = a.extend({}, c.defaultOptions.templates, this.options.templates);
393
+ this.options.originalPlacement = this.options.placement;
394
+ this.container = b.isElement(this.options.container) ? a(this.options.container) : false;
395
+ if (this.container === false) {
396
+ if (this.element.is(".dropdown-toggle")) {
397
+ this.container = a("~ .dropdown-menu:first", this.element);
398
+ } else {
399
+ this.container = this.element.is("input,textarea,button,.btn") ? this.element.parent() : this.element;
400
+ }
401
+ }
402
+ this.container.addClass("iconpicker-container");
403
+ if (this.isDropdownMenu()) {
404
+ this.options.templates.search = false;
405
+ this.options.templates.buttons = false;
406
+ this.options.placement = "inline";
407
+ }
408
+ this.input = this.element.is("input,textarea") ? this.element.addClass("iconpicker-input") : false;
409
+ if (this.input === false) {
410
+ this.input = this.container.find(this.options.input);
411
+ if (!this.input.is("input,textarea")) {
412
+ this.input = false;
413
+ }
414
+ }
415
+ this.component = this.isDropdownMenu() ? this.container.parent().find(this.options.component) : this.container.find(this.options.component);
416
+ if (this.component.length === 0) {
417
+ this.component = false;
418
+ } else {
419
+ this.component.find("i").addClass("iconpicker-component");
420
+ }
421
+ this._createPopover();
422
+ this._createIconpicker();
423
+ if (this.getAcceptButton().length === 0) {
424
+ this.options.mustAccept = false;
425
+ }
426
+ if (this.isInputGroup()) {
427
+ this.container.parent().append(this.popover);
428
+ } else {
429
+ this.container.append(this.popover);
430
+ }
431
+ this._bindElementEvents();
432
+ this._bindWindowEvents();
433
+ this.update(this.options.selected);
434
+ if (this.isInline()) {
435
+ this.show();
436
+ }
437
+ this._trigger("iconpickerCreated");
438
+ };
439
+ c._idCounter = 0;
440
+ c.defaultOptions = {
441
+ title: false,
442
+ selected: false,
443
+ defaultValue: false,
444
+ placement: "bottom",
445
+ collision: "none",
446
+ animation: true,
447
+ hideOnSelect: false,
448
+ showFooter: false,
449
+ searchInFooter: false,
450
+ mustAccept: false,
451
+ selectedCustomClass: "bg-primary",
452
+ icons: [],
453
+ fullClassFormatter: function(a) {
454
+ return "fa " + a;
455
+ },
456
+ input: "input,.iconpicker-input",
457
+ inputSearch: false,
458
+ container: false,
459
+ component: ".input-group-addon,.iconpicker-component",
460
+ templates: {
461
+ popover: '<div class="iconpicker-popover popover"><div class="arrow"></div>' + '<div class="popover-title"></div><div class="popover-content"></div></div>',
462
+ footer: '<div class="popover-footer"></div>',
463
+ buttons: '<button class="iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm">Cancel</button>' + ' <button class="iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm">Accept</button>',
464
+ search: '<input type="search" class="form-control iconpicker-search" placeholder="Type to filter" />',
465
+ iconpicker: '<div class="iconpicker"><div class="iconpicker-items"></div></div>',
466
+ iconpickerItem: '<a role="button" href="#" class="iconpicker-item"><i></i></a>'
467
+ }
468
+ };
469
+ c.batch = function(b, c) {
470
+ var d = Array.prototype.slice.call(arguments, 2);
471
+ return a(b).each(function() {
472
+ var b = a(this).data("iconpicker");
473
+ if (!!b) {
474
+ b[c].apply(b, d);
475
+ }
476
+ });
477
+ };
478
+ c.prototype = {
479
+ constructor: c,
480
+ options: {},
481
+ _id: 0,
482
+ _trigger: function(b, c) {
483
+ c = c || {};
484
+ this.element.trigger(a.extend({
485
+ type: b,
486
+ iconpickerInstance: this
487
+ }, c));
488
+ },
489
+ _createPopover: function() {
490
+ this.popover = a(this.options.templates.popover);
491
+ var c = this.popover.find(".popover-title");
492
+ if (!!this.options.title) {
493
+ c.append(a('<div class="popover-title-text">' + this.options.title + "</div>"));
494
+ }
495
+ if (this.hasSeparatedSearchInput() && !this.options.searchInFooter) {
496
+ c.append(this.options.templates.search);
497
+ } else if (!this.options.title) {
498
+ c.remove();
499
+ }
500
+ if (this.options.showFooter && !b.isEmpty(this.options.templates.footer)) {
501
+ var d = a(this.options.templates.footer);
502
+ if (this.hasSeparatedSearchInput() && this.options.searchInFooter) {
503
+ d.append(a(this.options.templates.search));
504
+ }
505
+ if (!b.isEmpty(this.options.templates.buttons)) {
506
+ d.append(a(this.options.templates.buttons));
507
+ }
508
+ this.popover.append(d);
509
+ }
510
+ if (this.options.animation === true) {
511
+ this.popover.addClass("fade");
512
+ }
513
+ return this.popover;
514
+ },
515
+ _createIconpicker: function() {
516
+ var b = this;
517
+ this.iconpicker = a(this.options.templates.iconpicker);
518
+ var c = function(c) {
519
+ var d = a(this);
520
+ if (d.is("i")) {
521
+ d = d.parent();
522
+ }
523
+ b._trigger("iconpickerSelect", {
524
+ iconpickerItem: d,
525
+ iconpickerValue: b.iconpickerValue
526
+ });
527
+ if (b.options.mustAccept === false) {
528
+ b.update(d.data("iconpickerValue"));
529
+ b._trigger("iconpickerSelected", {
530
+ iconpickerItem: this,
531
+ iconpickerValue: b.iconpickerValue
532
+ });
533
+ } else {
534
+ b.update(d.data("iconpickerValue"), true);
535
+ }
536
+ if (b.options.hideOnSelect && b.options.mustAccept === false) {
537
+ b.hide();
538
+ }
539
+ c.preventDefault();
540
+ return false;
541
+ };
542
+ for (var d in this.options.icons) {
543
+ if (typeof this.options.icons[d] === "string") {
544
+ var e = a(this.options.templates.iconpickerItem);
545
+ e.find("i").addClass(this.options.fullClassFormatter(this.options.icons[d]));
546
+ e.data("iconpickerValue", this.options.icons[d]).on("click.iconpicker", c);
547
+ this.iconpicker.find(".iconpicker-items").append(e.attr("title", "." + this.options.icons[d]));
548
+ }
549
+ }
550
+ this.popover.find(".popover-content").append(this.iconpicker);
551
+ return this.iconpicker;
552
+ },
553
+ _isEventInsideIconpicker: function(b) {
554
+ var c = a(b.target);
555
+ if ((!c.hasClass("iconpicker-element") || c.hasClass("iconpicker-element") && !c.is(this.element)) && c.parents(".iconpicker-popover").length === 0) {
556
+ return false;
557
+ }
558
+ return true;
559
+ },
560
+ _bindElementEvents: function() {
561
+ var c = this;
562
+ this.getSearchInput().on("keyup.iconpicker", function() {
563
+ c.filter(a(this).val().toLowerCase());
564
+ });
565
+ this.getAcceptButton().on("click.iconpicker", function() {
566
+ var a = c.iconpicker.find(".iconpicker-selected").get(0);
567
+ c.update(c.iconpickerValue);
568
+ c._trigger("iconpickerSelected", {
569
+ iconpickerItem: a,
570
+ iconpickerValue: c.iconpickerValue
571
+ });
572
+ if (!c.isInline()) {
573
+ c.hide();
574
+ }
575
+ });
576
+ this.getCancelButton().on("click.iconpicker", function() {
577
+ if (!c.isInline()) {
578
+ c.hide();
579
+ }
580
+ });
581
+ this.element.on("focus.iconpicker", function(a) {
582
+ c.show();
583
+ a.stopPropagation();
584
+ });
585
+ if (this.hasComponent()) {
586
+ this.component.on("click.iconpicker", function() {
587
+ c.toggle();
588
+ });
589
+ }
590
+ if (this.hasInput()) {
591
+ this.input.on("keyup.iconpicker", function(d) {
592
+ if (!b.inArray(d.keyCode, [ 38, 40, 37, 39, 16, 17, 18, 9, 8, 91, 93, 20, 46, 186, 190, 46, 78, 188, 44, 86 ])) {
593
+ c.update();
594
+ } else {
595
+ c._updateFormGroupStatus(c.getValid(this.value) !== false);
596
+ }
597
+ if (c.options.inputSearch === true) {
598
+ c.filter(a(this).val().toLowerCase());
599
+ }
600
+ });
601
+ }
602
+ },
603
+ _bindWindowEvents: function() {
604
+ var b = a(window.document);
605
+ var c = this;
606
+ var d = ".iconpicker.inst" + this._id;
607
+ a(window).on("resize.iconpicker" + d + " orientationchange.iconpicker" + d, function(a) {
608
+ if (c.popover.hasClass("in")) {
609
+ c.updatePlacement();
610
+ }
611
+ });
612
+ if (!c.isInline()) {
613
+ b.on("mouseup" + d, function(a) {
614
+ if (!c._isEventInsideIconpicker(a) && !c.isInline()) {
615
+ c.hide();
616
+ }
617
+ a.stopPropagation();
618
+ a.preventDefault();
619
+ return false;
620
+ });
621
+ }
622
+ return false;
623
+ },
624
+ _unbindElementEvents: function() {
625
+ this.popover.off(".iconpicker");
626
+ this.element.off(".iconpicker");
627
+ if (this.hasInput()) {
628
+ this.input.off(".iconpicker");
629
+ }
630
+ if (this.hasComponent()) {
631
+ this.component.off(".iconpicker");
632
+ }
633
+ if (this.hasContainer()) {
634
+ this.container.off(".iconpicker");
635
+ }
636
+ },
637
+ _unbindWindowEvents: function() {
638
+ a(window).off(".iconpicker.inst" + this._id);
639
+ a(window.document).off(".iconpicker.inst" + this._id);
640
+ },
641
+ updatePlacement: function(b, c) {
642
+ b = b || this.options.placement;
643
+ this.options.placement = b;
644
+ c = c || this.options.collision;
645
+ c = c === true ? "flip" : c;
646
+ var d = {
647
+ at: "right bottom",
648
+ my: "right top",
649
+ of: this.hasInput() && !this.isInputGroup() ? this.input : this.container,
650
+ collision: c === true ? "flip" : c,
651
+ within: window
652
+ };
653
+ this.popover.removeClass("inline topLeftCorner topLeft top topRight topRightCorner " + "rightTop right rightBottom bottomRight bottomRightCorner " + "bottom bottomLeft bottomLeftCorner leftBottom left leftTop");
654
+ if (typeof b === "object") {
655
+ return this.popover.pos(a.extend({}, d, b));
656
+ }
657
+ switch (b) {
658
+ case "inline":
659
+ {
660
+ d = false;
661
+ }
662
+ break;
663
+
664
+ case "topLeftCorner":
665
+ {
666
+ d.my = "right bottom";
667
+ d.at = "left top";
668
+ }
669
+ break;
670
+
671
+ case "topLeft":
672
+ {
673
+ d.my = "left bottom";
674
+ d.at = "left top";
675
+ }
676
+ break;
677
+
678
+ case "top":
679
+ {
680
+ d.my = "center bottom";
681
+ d.at = "center top";
682
+ }
683
+ break;
684
+
685
+ case "topRight":
686
+ {
687
+ d.my = "right bottom";
688
+ d.at = "right top";
689
+ }
690
+ break;
691
+
692
+ case "topRightCorner":
693
+ {
694
+ d.my = "left bottom";
695
+ d.at = "right top";
696
+ }
697
+ break;
698
+
699
+ case "rightTop":
700
+ {
701
+ d.my = "left bottom";
702
+ d.at = "right center";
703
+ }
704
+ break;
705
+
706
+ case "right":
707
+ {
708
+ d.my = "left center";
709
+ d.at = "right center";
710
+ }
711
+ break;
712
+
713
+ case "rightBottom":
714
+ {
715
+ d.my = "left top";
716
+ d.at = "right center";
717
+ }
718
+ break;
719
+
720
+ case "bottomRightCorner":
721
+ {
722
+ d.my = "left top";
723
+ d.at = "right bottom";
724
+ }
725
+ break;
726
+
727
+ case "bottomRight":
728
+ {
729
+ d.my = "right top";
730
+ d.at = "right bottom";
731
+ }
732
+ break;
733
+
734
+ case "bottom":
735
+ {
736
+ d.my = "center top";
737
+ d.at = "center bottom";
738
+ }
739
+ break;
740
+
741
+ case "bottomLeft":
742
+ {
743
+ d.my = "left top";
744
+ d.at = "left bottom";
745
+ }
746
+ break;
747
+
748
+ case "bottomLeftCorner":
749
+ {
750
+ d.my = "right top";
751
+ d.at = "left bottom";
752
+ }
753
+ break;
754
+
755
+ case "leftBottom":
756
+ {
757
+ d.my = "right top";
758
+ d.at = "left center";
759
+ }
760
+ break;
761
+
762
+ case "left":
763
+ {
764
+ d.my = "right center";
765
+ d.at = "left center";
766
+ }
767
+ break;
768
+
769
+ case "leftTop":
770
+ {
771
+ d.my = "right bottom";
772
+ d.at = "left center";
773
+ }
774
+ break;
775
+
776
+ default:
777
+ {
778
+ return false;
779
+ }
780
+ break;
781
+ }
782
+ this.popover.css({
783
+ display: this.options.placement === "inline" ? "" : "block"
784
+ });
785
+ if (d !== false) {
786
+ this.popover.pos(d).css("maxWidth", a(window).width() - this.container.offset().left - 5);
787
+ } else {
788
+ this.popover.css({
789
+ top: "auto",
790
+ right: "auto",
791
+ bottom: "auto",
792
+ left: "auto",
793
+ maxWidth: "none"
794
+ });
795
+ }
796
+ this.popover.addClass(this.options.placement);
797
+ return true;
798
+ },
799
+ _updateComponents: function() {
800
+ this.iconpicker.find(".iconpicker-item.iconpicker-selected").removeClass("iconpicker-selected " + this.options.selectedCustomClass);
801
+ if (this.iconpickerValue) {
802
+ this.iconpicker.find("." + this.options.fullClassFormatter(this.iconpickerValue).replace(/ /g, ".")).parent().addClass("iconpicker-selected " + this.options.selectedCustomClass);
803
+ }
804
+ if (this.hasComponent()) {
805
+ var a = this.component.find("i");
806
+ if (a.length > 0) {
807
+ a.attr("class", this.options.fullClassFormatter(this.iconpickerValue));
808
+ } else {
809
+ this.component.html(this.getHtml());
810
+ }
811
+ }
812
+ },
813
+ _updateFormGroupStatus: function(a) {
814
+ if (this.hasInput()) {
815
+ if (a !== false) {
816
+ this.input.parents(".form-group:first").removeClass("has-error");
817
+ } else {
818
+ this.input.parents(".form-group:first").addClass("has-error");
819
+ }
820
+ return true;
821
+ }
822
+ return false;
823
+ },
824
+ getValid: function(c) {
825
+ if (!b.isString(c)) {
826
+ c = "";
827
+ }
828
+ var d = c === "";
829
+ c = a.trim(c);
830
+ if (b.inArray(c, this.options.icons) || d) {
831
+ return c;
832
+ }
833
+ return false;
834
+ },
835
+ setValue: function(a) {
836
+ var b = this.getValid(a);
837
+ if (b !== false) {
838
+ this.iconpickerValue = b;
839
+ this._trigger("iconpickerSetValue", {
840
+ iconpickerValue: b
841
+ });
842
+ return this.iconpickerValue;
843
+ } else {
844
+ this._trigger("iconpickerInvalid", {
845
+ iconpickerValue: a
846
+ });
847
+ return false;
848
+ }
849
+ },
850
+ getHtml: function() {
851
+ return '<i class="' + this.options.fullClassFormatter(this.iconpickerValue) + '"></i>';
852
+ },
853
+ setSourceValue: function(a) {
854
+ a = this.setValue(a);
855
+ if (a !== false && a !== "") {
856
+ if (this.hasInput()) {
857
+ this.input.val(this.iconpickerValue);
858
+ } else {
859
+ this.element.data("iconpickerValue", this.iconpickerValue);
860
+ }
861
+ this._trigger("iconpickerSetSourceValue", {
862
+ iconpickerValue: a
863
+ });
864
+ }
865
+ return a;
866
+ },
867
+ getSourceValue: function(a) {
868
+ a = a || this.options.defaultValue;
869
+ var b = a;
870
+ if (this.hasInput()) {
871
+ b = this.input.val();
872
+ } else {
873
+ b = this.element.data("iconpickerValue");
874
+ }
875
+ if (b === undefined || b === "" || b === null || b === false) {
876
+ b = a;
877
+ }
878
+ return b;
879
+ },
880
+ hasInput: function() {
881
+ return this.input !== false;
882
+ },
883
+ isInputSearch: function() {
884
+ return this.hasInput() && this.options.inputSearch === true;
885
+ },
886
+ isInputGroup: function() {
887
+ return this.container.is(".input-group");
888
+ },
889
+ isDropdownMenu: function() {
890
+ return this.container.is(".dropdown-menu");
891
+ },
892
+ hasSeparatedSearchInput: function() {
893
+ return this.options.templates.search !== false && !this.isInputSearch();
894
+ },
895
+ hasComponent: function() {
896
+ return this.component !== false;
897
+ },
898
+ hasContainer: function() {
899
+ return this.container !== false;
900
+ },
901
+ getAcceptButton: function() {
902
+ return this.popover.find(".iconpicker-btn-accept");
903
+ },
904
+ getCancelButton: function() {
905
+ return this.popover.find(".iconpicker-btn-cancel");
906
+ },
907
+ getSearchInput: function() {
908
+ return this.popover.find(".iconpicker-search");
909
+ },
910
+ filter: function(c) {
911
+ if (b.isEmpty(c)) {
912
+ this.iconpicker.find(".iconpicker-item").show();
913
+ return a(false);
914
+ } else {
915
+ var d = [];
916
+ this.iconpicker.find(".iconpicker-item").each(function() {
917
+ var b = a(this);
918
+ var e = b.attr("title").toLowerCase();
919
+ var f = false;
920
+ try {
921
+ f = new RegExp(c, "g");
922
+ } catch (a) {
923
+ f = false;
924
+ }
925
+ if (f !== false && e.match(f)) {
926
+ d.push(b);
927
+ b.show();
928
+ } else {
929
+ b.hide();
930
+ }
931
+ });
932
+ return d;
933
+ }
934
+ },
935
+ show: function() {
936
+ if (this.popover.hasClass("in")) {
937
+ return false;
938
+ }
939
+ a.iconpicker.batch(a(".iconpicker-popover.in:not(.inline)").not(this.popover), "hide");
940
+ this._trigger("iconpickerShow");
941
+ this.updatePlacement();
942
+ this.popover.addClass("in");
943
+ setTimeout(a.proxy(function() {
944
+ this.popover.css("display", this.isInline() ? "" : "block");
945
+ this._trigger("iconpickerShown");
946
+ }, this), this.options.animation ? 300 : 1);
947
+ },
948
+ hide: function() {
949
+ if (!this.popover.hasClass("in")) {
950
+ return false;
951
+ }
952
+ this._trigger("iconpickerHide");
953
+ this.popover.removeClass("in");
954
+ setTimeout(a.proxy(function() {
955
+ this.popover.css("display", "none");
956
+ this.getSearchInput().val("");
957
+ this.filter("");
958
+ this._trigger("iconpickerHidden");
959
+ }, this), this.options.animation ? 300 : 1);
960
+ },
961
+ toggle: function() {
962
+ if (this.popover.is(":visible")) {
963
+ this.hide();
964
+ } else {
965
+ this.show(true);
966
+ }
967
+ },
968
+ update: function(a, b) {
969
+ a = a ? a : this.getSourceValue(this.iconpickerValue);
970
+ this._trigger("iconpickerUpdate");
971
+ if (b === true) {
972
+ a = this.setValue(a);
973
+ } else {
974
+ a = this.setSourceValue(a);
975
+ this._updateFormGroupStatus(a !== false);
976
+ }
977
+ if (a !== false) {
978
+ this._updateComponents();
979
+ }
980
+ this._trigger("iconpickerUpdated");
981
+ return a;
982
+ },
983
+ destroy: function() {
984
+ this._trigger("iconpickerDestroy");
985
+ this.element.removeData("iconpicker").removeData("iconpickerValue").removeClass("iconpicker-element");
986
+ this._unbindElementEvents();
987
+ this._unbindWindowEvents();
988
+ a(this.popover).remove();
989
+ this._trigger("iconpickerDestroyed");
990
+ },
991
+ disable: function() {
992
+ if (this.hasInput()) {
993
+ this.input.prop("disabled", true);
994
+ return true;
995
+ }
996
+ return false;
997
+ },
998
+ enable: function() {
999
+ if (this.hasInput()) {
1000
+ this.input.prop("disabled", false);
1001
+ return true;
1002
+ }
1003
+ return false;
1004
+ },
1005
+ isDisabled: function() {
1006
+ if (this.hasInput()) {
1007
+ return this.input.prop("disabled") === true;
1008
+ }
1009
+ return false;
1010
+ },
1011
+ isInline: function() {
1012
+ return this.options.placement === "inline" || this.popover.hasClass("inline");
1013
+ }
1014
+ };
1015
+ a.iconpicker = c;
1016
+ a.fn.iconpicker = function(b) {
1017
+ return this.each(function() {
1018
+ var d = a(this);
1019
+ if (!d.data("iconpicker")) {
1020
+ d.data("iconpicker", new c(this, typeof b === "object" ? b : {}));
1021
+ }
1022
+ });
1023
+ };
1024
+ c.defaultOptions.fullClassFormatter = function(val) {
1025
+ return val;
1026
+ };
1027
+ c.defaultOptions.icons = [ "fas fa-address-book","fas fa-address-card","fas fa-adjust","fas fa-align-center","fas fa-align-justify","fas fa-align-left","fas fa-align-right","fas fa-ambulance","fas fa-american-sign-language-interpreting","fas fa-anchor","fas fa-angle-double-down","fas fa-angle-double-left","fas fa-angle-double-right","fas fa-angle-double-up","fas fa-angle-down","fas fa-angle-left","fas fa-angle-right","fas fa-angle-up","fas fa-archive","fas fa-arrow-alt-circle-down","fas fa-arrow-alt-circle-left","fas fa-arrow-alt-circle-right","fas fa-arrow-alt-circle-up","fas fa-arrow-circle-down","fas fa-arrow-circle-left","fas fa-arrow-circle-right","fas fa-arrow-circle-up","fas fa-arrow-down","fas fa-arrow-left","fas fa-arrow-right","fas fa-arrow-up","fas fa-arrows-alt-h","fas fa-arrows-alt-v","fas fa-arrows-alt","fas fa-assistive-listening-systems","fas fa-asterisk","fas fa-at","fas fa-audio-description","fas fa-backward","fas fa-balance-scale","fas fa-ban","fas fa-barcode","fas fa-bars","fas fa-baseball-ball","fas fa-basketball-ball","fas fa-bath","fas fa-battery-empty","fas fa-battery-full","fas fa-battery-half","fas fa-battery-quarter","fas fa-battery-three-quarters","fas fa-bed","fas fa-beer","fas fa-bell-slash","fas fa-bell","fas fa-bicycle","fas fa-binoculars","fas fa-birthday-cake","fas fa-blind","fas fa-bold","fas fa-bolt","fas fa-bomb","fas fa-book","fas fa-bookmark","fas fa-bowling-ball","fas fa-braille","fas fa-briefcase","fas fa-bug","fas fa-building","fas fa-bullhorn","fas fa-bullseye","fas fa-bus","fas fa-calculator","fas fa-calendar-alt","fas fa-calendar-check","fas fa-calendar-minus","fas fa-calendar-plus","fas fa-calendar-times","fas fa-calendar","fas fa-camera-retro","fas fa-camera","fas fa-car","fas fa-caret-down","fas fa-caret-left","fas fa-caret-right","fas fa-caret-square-down","fas fa-caret-square-left","fas fa-caret-square-right","fas fa-caret-square-up","fas fa-caret-up","fas fa-cart-arrow-down","fas fa-cart-plus","fas fa-certificate","fas fa-chart-area","fas fa-chart-bar","fas fa-chart-line","fas fa-chart-pie","fas fa-check-circle","fas fa-check-square","fas fa-check","fas fa-chess-bishop","fas fa-chess-board","fas fa-chess-king","fas fa-chess-knight","fas fa-chess-pawn","fas fa-chess-queen","fas fa-chess-rook","fas fa-chess","fas fa-chevron-circle-down","fas fa-chevron-circle-left","fas fa-chevron-circle-right","fas fa-chevron-circle-up","fas fa-chevron-down","fas fa-chevron-left","fas fa-chevron-right","fas fa-chevron-up","fas fa-child","fas fa-circle-notch","fas fa-circle","fas fa-clipboard","fas fa-clock","fas fa-clone","fas fa-closed-captioning","fas fa-cloud-download-alt","fas fa-cloud-upload-alt","fas fa-cloud","fas fa-code-branch","fas fa-code","fas fa-coffee","fas fa-cog","fas fa-cogs","fas fa-columns","fas fa-comment-alt","fas fa-comment","fas fa-comments","fas fa-compass","fas fa-compress","fas fa-copy","fas fa-copyright","fas fa-credit-card","fas fa-crop","fas fa-crosshairs","fas fa-cube","fas fa-cubes","fas fa-cut","fas fa-database","fas fa-deaf","fas fa-desktop","fas fa-dollar-sign","fas fa-dot-circle","fas fa-download","fas fa-edit","fas fa-eject","fas fa-ellipsis-h","fas fa-ellipsis-v","fas fa-envelope-open","fas fa-envelope-square","fas fa-envelope","fas fa-eraser","fas fa-euro-sign","fas fa-exchange-alt","fas fa-exclamation-circle","fas fa-exclamation-triangle","fas fa-exclamation","fas fa-expand-arrows-alt","fas fa-expand","fas fa-external-link-alt","fas fa-external-link-square-alt","fas fa-eye-dropper","fas fa-eye-slash","fas fa-eye","fas fa-fast-backward","fas fa-fast-forward","fas fa-fax","fas fa-female","fas fa-fighter-jet","fas fa-file-alt","fas fa-file-archive","fas fa-file-audio","fas fa-file-code","fas fa-file-excel","fas fa-file-image","fas fa-file-pdf","fas fa-file-powerpoint","fas fa-file-video","fas fa-file-word","fas fa-file","fas fa-film","fas fa-filter","fas fa-fire-extinguisher","fas fa-fire","fas fa-flag-checkered","fas fa-flag","fas fa-flask","fas fa-folder-open","fas fa-folder","fas fa-font","fas fa-football-ball","fas fa-forward","fas fa-frown","fas fa-futbol","fas fa-gamepad","fas fa-gavel","fas fa-gem","fas fa-genderless","fas fa-gift","fas fa-glass-martini","fas fa-globe","fas fa-golf-ball","fas fa-graduation-cap","fas fa-h-square","fas fa-hand-lizard","fas fa-hand-paper","fas fa-hand-peace","fas fa-hand-point-down","fas fa-hand-point-left","fas fa-hand-point-right","fas fa-hand-point-up","fas fa-hand-pointer","fas fa-hand-rock","fas fa-hand-scissors","fas fa-hand-spock","fas fa-handshake","fas fa-hashtag","fas fa-hdd","fas fa-heading","fas fa-headphones","fas fa-heart","fas fa-heartbeat","fas fa-history","fas fa-hockey-puck","fas fa-home","fas fa-hospital","fas fa-hourglass-end","fas fa-hourglass-half","fas fa-hourglass-start","fas fa-hourglass","fas fa-i-cursor","fas fa-id-badge","fas fa-id-card","fas fa-image","fas fa-images","fas fa-inbox","fas fa-indent","fas fa-industry","fas fa-info-circle","fas fa-info","fas fa-italic","fas fa-key","fas fa-keyboard","fas fa-language","fas fa-laptop","fas fa-leaf","fas fa-lemon","fas fa-level-down-alt","fas fa-level-up-alt","fas fa-life-ring","fas fa-lightbulb","fas fa-link","fas fa-lira-sign","fas fa-list-alt","fas fa-list-ol","fas fa-list-ul","fas fa-list","fas fa-location-arrow","fas fa-lock-open","fas fa-lock","fas fa-long-arrow-alt-down","fas fa-long-arrow-alt-left","fas fa-long-arrow-alt-right","fas fa-long-arrow-alt-up","fas fa-low-vision","fas fa-magic","fas fa-magnet","fas fa-male","fas fa-map-marker-alt","fas fa-map-marker","fas fa-map-pin","fas fa-map-signs","fas fa-map","fas fa-mars-double","fas fa-mars-stroke-h","fas fa-mars-stroke-v","fas fa-mars-stroke","fas fa-mars","fas fa-medkit","fas fa-meh","fas fa-mercury","fas fa-microchip","fas fa-microphone-slash","fas fa-microphone","fas fa-minus-circle","fas fa-minus-square","fas fa-minus","fas fa-mobile-alt","fas fa-mobile","fas fa-money-bill-alt","fas fa-moon","fas fa-motorcycle","fas fa-mouse-pointer","fas fa-music","fas fa-neuter","fas fa-newspaper","fas fa-object-group","fas fa-object-ungroup","fas fa-outdent","fas fa-paint-brush","fas fa-paper-plane","fas fa-paperclip","fas fa-paragraph","fas fa-paste","fas fa-pause-circle","fas fa-pause","fas fa-paw","fas fa-pen-square","fas fa-pencil-alt","fas fa-percent","fas fa-phone-square","fas fa-phone-volume","fas fa-phone","fas fa-plane","fas fa-play-circle","fas fa-play","fas fa-plug","fas fa-plus-circle","fas fa-plus-square","fas fa-plus","fas fa-podcast","fas fa-pound-sign","fas fa-power-off","fas fa-print","fas fa-puzzle-piece","fas fa-qrcode","fas fa-question-circle","fas fa-question","fas fa-quidditch","fas fa-quote-left","fas fa-quote-right","fas fa-random","fas fa-recycle","fas fa-redo-alt","fas fa-redo","fas fa-registered","fas fa-reply-all","fas fa-reply","fas fa-retweet","fas fa-road","fas fa-rocket","fas fa-rss-square","fas fa-rss","fas fa-ruble-sign","fas fa-rupee-sign","fas fa-save","fas fa-search-minus","fas fa-search-plus","fas fa-search","fas fa-server","fas fa-share-alt-square","fas fa-share-alt","fas fa-share-square","fas fa-share","fas fa-shekel-sign","fas fa-shield-alt","fas fa-ship","fas fa-shopping-bag","fas fa-shopping-basket","fas fa-shopping-cart","fas fa-shower","fas fa-sign-in-alt","fas fa-sign-language","fas fa-sign-out-alt","fas fa-signal","fas fa-sitemap","fas fa-sliders-h","fas fa-smile","fas fa-snowflake","fas fa-sort-alpha-down","fas fa-sort-alpha-up","fas fa-sort-amount-down","fas fa-sort-amount-up","fas fa-sort-down","fas fa-sort-numeric-down","fas fa-sort-numeric-up","fas fa-sort-up","fas fa-sort","fas fa-space-shuttle","fas fa-spinner","fas fa-square-full","fas fa-square","fas fa-star-half","fas fa-star","fas fa-step-backward","fas fa-step-forward","fas fa-stethoscope","fas fa-sticky-note","fas fa-stop-circle","fas fa-stop","fas fa-stopwatch","fas fa-street-view","fas fa-strikethrough","fas fa-subscript","fas fa-subway","fas fa-suitcase","fas fa-sun","fas fa-superscript","fas fa-sync-alt","fas fa-sync","fas fa-table-tennis","fas fa-table","fas fa-tablet-alt","fas fa-tablet","fas fa-tachometer-alt","fas fa-tag","fas fa-tags","fas fa-tasks","fas fa-taxi","fas fa-terminal","fas fa-text-height","fas fa-text-width","fas fa-th-large","fas fa-th-list","fas fa-th","fas fa-thermometer-empty","fas fa-thermometer-full","fas fa-thermometer-half","fas fa-thermometer-quarter","fas fa-thermometer-three-quarters","fas fa-thumbs-down","fas fa-thumbs-up","fas fa-thumbtack","fas fa-ticket-alt","fas fa-times-circle","fas fa-times","fas fa-tint","fas fa-toggle-off","fas fa-toggle-on","fas fa-trademark","fas fa-train","fas fa-transgender-alt","fas fa-transgender","fas fa-trash-alt","fas fa-trash","fas fa-tree","fas fa-trophy","fas fa-truck","fas fa-tty","fas fa-tv","fas fa-umbrella","fas fa-underline","fas fa-undo-alt","fas fa-undo","fas fa-universal-access","fas fa-university","fas fa-unlink","fas fa-unlock-alt","fas fa-unlock","fas fa-upload","fas fa-user-circle","fas fa-user-md","fas fa-user-plus","fas fa-user-secret","fas fa-user-times","fas fa-user","fas fa-users","fas fa-utensil-spoon","fas fa-utensils","fas fa-venus-double","fas fa-venus-mars","fas fa-venus","fas fa-video","fas fa-volleyball-ball","fas fa-volume-down","fas fa-volume-off","fas fa-volume-up","fas fa-wheelchair","fas fa-wifi","fas fa-window-close","fas fa-window-maximize","fas fa-window-minimize","fas fa-window-restore","fas fa-won-sign","fas fa-wrench","fas fa-yen-sign","far fa-address-book","far fa-address-card","far fa-arrow-alt-circle-down","far fa-arrow-alt-circle-left","far fa-arrow-alt-circle-right","far fa-arrow-alt-circle-up","far fa-bell-slash","far fa-bell","far fa-bookmark","far fa-building","far fa-calendar-alt","far fa-calendar-check","far fa-calendar-minus","far fa-calendar-plus","far fa-calendar-times","far fa-calendar","far fa-caret-square-down","far fa-caret-square-left","far fa-caret-square-right","far fa-caret-square-up","far fa-chart-bar","far fa-check-circle","far fa-check-square","far fa-circle","far fa-clipboard","far fa-clock","far fa-clone","far fa-closed-captioning","far fa-comment-alt","far fa-comment","far fa-comments","far fa-compass","far fa-copy","far fa-copyright","far fa-credit-card","far fa-dot-circle","far fa-edit","far fa-envelope-open","far fa-envelope","far fa-eye-slash","far fa-file-alt","far fa-file-archive","far fa-file-audio","far fa-file-code","far fa-file-excel","far fa-file-image","far fa-file-pdf","far fa-file-powerpoint","far fa-file-video","far fa-file-word","far fa-file","far fa-flag","far fa-folder-open","far fa-folder","far fa-frown","far fa-futbol","far fa-gem","far fa-hand-lizard","far fa-hand-paper","far fa-hand-peace","far fa-hand-point-down","far fa-hand-point-left","far fa-hand-point-right","far fa-hand-point-up","far fa-hand-pointer","far fa-hand-rock","far fa-hand-scissors","far fa-hand-spock","far fa-handshake","far fa-hdd","far fa-heart","far fa-hospital","far fa-hourglass","far fa-id-badge","far fa-id-card","far fa-image","far fa-images","far fa-keyboard","far fa-lemon","far fa-life-ring","far fa-lightbulb","far fa-list-alt","far fa-map","far fa-meh","far fa-minus-square","far fa-money-bill-alt","far fa-moon","far fa-newspaper","far fa-object-group","far fa-object-ungroup","far fa-paper-plane","far fa-pause-circle","far fa-play-circle","far fa-plus-square","far fa-question-circle","far fa-registered","far fa-save","far fa-share-square","far fa-smile","far fa-snowflake","far fa-square","far fa-star-half","far fa-star","far fa-sticky-note","far fa-stop-circle","far fa-sun","far fa-thumbs-down","far fa-thumbs-up","far fa-times-circle","far fa-trash-alt","far fa-user-circle","far fa-user","far fa-window-close","far fa-window-maximize","far fa-window-minimize","far fa-window-restore","fab fa-500px","fab fa-accessible-icon","fab fa-accusoft","fab fa-adn","fab fa-adversal","fab fa-affiliatetheme","fab fa-algolia","fab fa-amazon-pay","fab fa-amazon","fab fa-amilia","fab fa-android","fab fa-angellist","fab fa-angrycreative","fab fa-angular","fab fa-app-store-ios","fab fa-app-store","fab fa-apper","fab fa-apple-pay","fab fa-apple","fab fa-asymmetrik","fab fa-audible","fab fa-autoprefixer","fab fa-avianex","fab fa-aviato","fab fa-aws","fab fa-bandcamp","fab fa-behance-square","fab fa-behance","fab fa-bimobject","fab fa-bitbucket","fab fa-bitcoin","fab fa-bity","fab fa-black-tie","fab fa-blackberry","fab fa-blogger-b","fab fa-blogger","fab fa-bluetooth-b","fab fa-bluetooth","fab fa-btc","fab fa-buromobelexperte","fab fa-buysellads","fab fa-cc-amazon-pay","fab fa-cc-amex","fab fa-cc-apple-pay","fab fa-cc-diners-club","fab fa-cc-discover","fab fa-cc-jcb","fab fa-cc-mastercard","fab fa-cc-paypal","fab fa-cc-stripe","fab fa-cc-visa","fab fa-centercode","fab fa-chrome","fab fa-cloudscale","fab fa-cloudsmith","fab fa-cloudversify","fab fa-codepen","fab fa-codiepie","fab fa-connectdevelop","fab fa-contao","fab fa-cpanel","fab fa-creative-commons","fab fa-css3-alt","fab fa-css3","fab fa-cuttlefish","fab fa-d-and-d","fab fa-dashcube","fab fa-delicious","fab fa-deploydog","fab fa-deskpro","fab fa-deviantart","fab fa-digg","fab fa-digital-ocean","fab fa-discord","fab fa-discourse","fab fa-dochub","fab fa-docker","fab fa-draft2digital","fab fa-dribbble-square","fab fa-dribbble","fab fa-dropbox","fab fa-drupal","fab fa-dyalog","fab fa-earlybirds","fab fa-edge","fab fa-elementor","fab fa-ember","fab fa-empire","fab fa-envira","fab fa-erlang","fab fa-ethereum","fab fa-etsy","fab fa-expeditedssl","fab fa-facebook-f","fab fa-facebook-messenger","fab fa-facebook-square","fab fa-facebook","fab fa-firefox","fab fa-first-order","fab fa-firstdraft","fab fa-flickr","fab fa-flipboard","fab fa-fly","fab fa-font-awesome-alt","fab fa-font-awesome-flag","fab fa-font-awesome","fab fa-fonticons-fi","fab fa-fonticons","fab fa-fort-awesome-alt","fab fa-fort-awesome","fab fa-forumbee","fab fa-foursquare","fab fa-free-code-camp","fab fa-freebsd","fab fa-get-pocket","fab fa-gg-circle","fab fa-gg","fab fa-git-square","fab fa-git","fab fa-github-alt","fab fa-github-square","fab fa-github","fab fa-gitkraken","fab fa-gitlab","fab fa-gitter","fab fa-glide-g","fab fa-glide","fab fa-gofore","fab fa-goodreads-g","fab fa-goodreads","fab fa-google-drive","fab fa-google-play","fab fa-google-plus-g","fab fa-google-plus-square","fab fa-google-plus","fab fa-google-wallet","fab fa-google","fab fa-gratipay","fab fa-grav","fab fa-gripfire","fab fa-grunt","fab fa-gulp","fab fa-hacker-news-square","fab fa-hacker-news","fab fa-hips","fab fa-hire-a-helper","fab fa-hooli","fab fa-hotjar","fab fa-houzz","fab fa-html5","fab fa-hubspot","fab fa-imdb","fab fa-instagram","fab fa-internet-explorer","fab fa-ioxhost","fab fa-itunes-note","fab fa-itunes","fab fa-jenkins","fab fa-joget","fab fa-joomla","fab fa-js-square","fab fa-js","fab fa-jsfiddle","fab fa-keycdn","fab fa-kickstarter-k","fab fa-kickstarter","fab fa-korvue","fab fa-laravel","fab fa-lastfm-square","fab fa-lastfm","fab fa-leanpub","fab fa-less","fab fa-line","fab fa-linkedin-in","fab fa-linkedin","fab fa-linode","fab fa-linux","fab fa-lyft","fab fa-magento","fab fa-maxcdn","fab fa-medapps","fab fa-medium-m","fab fa-medium","fab fa-medrt","fab fa-meetup","fab fa-microsoft","fab fa-mix","fab fa-mixcloud","fab fa-mizuni","fab fa-modx","fab fa-monero","fab fa-napster","fab fa-nintendo-switch","fab fa-node-js","fab fa-node","fab fa-npm","fab fa-ns8","fab fa-nutritionix","fab fa-odnoklassniki-square","fab fa-odnoklassniki","fab fa-opencart","fab fa-openid","fab fa-opera","fab fa-optin-monster","fab fa-osi","fab fa-page4","fab fa-pagelines","fab fa-palfed","fab fa-patreon","fab fa-paypal","fab fa-periscope","fab fa-phabricator","fab fa-phoenix-framework","fab fa-php","fab fa-pied-piper-alt","fab fa-pied-piper-pp","fab fa-pied-piper","fab fa-pinterest-p","fab fa-pinterest-square","fab fa-pinterest","fab fa-playstation","fab fa-product-hunt","fab fa-pushed","fab fa-python","fab fa-qq","fab fa-quinscape","fab fa-quora","fab fa-ravelry","fab fa-react","fab fa-rebel","fab fa-red-river","fab fa-reddit-alien","fab fa-reddit-square","fab fa-reddit","fab fa-rendact","fab fa-renren","fab fa-replyd","fab fa-resolving","fab fa-rocketchat","fab fa-rockrms","fab fa-safari","fab fa-sass","fab fa-schlix","fab fa-scribd","fab fa-searchengin","fab fa-sellcast","fab fa-sellsy","fab fa-servicestack","fab fa-shirtsinbulk","fab fa-simplybuilt","fab fa-sistrix","fab fa-skyatlas","fab fa-skype","fab fa-slack-hash","fab fa-slack","fab fa-slideshare","fab fa-snapchat-ghost","fab fa-snapchat-square","fab fa-snapchat","fab fa-soundcloud","fab fa-speakap","fab fa-spotify","fab fa-stack-exchange","fab fa-stack-overflow","fab fa-staylinked","fab fa-steam-square","fab fa-steam-symbol","fab fa-steam","fab fa-sticker-mule","fab fa-strava","fab fa-stripe-s","fab fa-stripe","fab fa-studiovinari","fab fa-stumbleupon-circle","fab fa-stumbleupon","fab fa-superpowers","fab fa-supple","fab fa-telegram-plane","fab fa-telegram","fab fa-tencent-weibo","fab fa-themeisle","fab fa-trello","fab fa-tripadvisor","fab fa-tumblr-square","fab fa-tumblr","fab fa-twitch","fab fa-twitter-square","fab fa-twitter","fab fa-typo3","fab fa-uber","fab fa-uikit","fab fa-uniregistry","fab fa-untappd","fab fa-usb","fab fa-ussunnah","fab fa-vaadin","fab fa-viacoin","fab fa-viadeo-square","fab fa-viadeo","fab fa-viber","fab fa-vimeo-square","fab fa-vimeo-v","fab fa-vimeo","fab fa-vine","fab fa-vk","fab fa-vnv","fab fa-vuejs","fab fa-weibo","fab fa-weixin","fab fa-whatsapp-square","fab fa-whatsapp","fab fa-whmcs","fab fa-wikipedia-w","fab fa-windows","fab fa-wordpress-simple","fab fa-wordpress","fab fa-wpbeginner","fab fa-wpexplorer","fab fa-wpforms","fab fa-xbox","fab fa-xing-square","fab fa-xing","fab fa-y-combinator","fab fa-yahoo","fab fa-yandex-international","fab fa-yandex","fab fa-yelp","fab fa-yoast","fab fa-youtube-square","fab fa-youtube" ];
1028
+ c.defaultOptions.templates = {
1029
+ popover: '<div class="iconpicker-popover popover"><div class="arrow"></div>' +
1030
+ '<div class="popover-title"></div><div class="popover-content"></div></div>',
1031
+ footer: '<div class="popover-footer"></div>',
1032
+ buttons: '<button class="iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm">Cancel</button>' +
1033
+ ' <button class="iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm">Accept</button>',
1034
+ search: '<input type="search" class="form-control iconpicker-search" placeholder="Type to filter" />',
1035
+ iconpicker: '<div class="iconpicker"><div class="iconpicker-items"></div></div>',
1036
+ iconpickerItem: '<a role="button" href="#" class="iconpicker-item"><i></i></a>',
1037
+ };
1038
+ });
admin/assets/third-party/iconpicker/fontawesome-iconpicker.min.css ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Font Awesome Icon Picker
3
+ * https://farbelous.github.io/fontawesome-iconpicker/
4
+ *
5
+ * Originally written by (c) 2016 Javi Aguilar
6
+ * Licensed under the MIT License
7
+ * https://github.com/farbelous/fontawesome-iconpicker/blob/master/LICENSE
8
+ *
9
+ */.iconpicker-popover.popover{position:absolute;top:0;left:0;display:none;max-width:none;padding:1px;text-align:left;width:300px;background:#f7f7f7;}.iconpicker-popover.popover.top,.iconpicker-popover.popover.topLeftCorner,.iconpicker-popover.popover.topLeft,.iconpicker-popover.popover.topRight,.iconpicker-popover.popover.topRightCorner{margin-top:-10px}.iconpicker-popover.popover.right,.iconpicker-popover.popover.rightTop,.iconpicker-popover.popover.rightBottom{margin-left:10px}.iconpicker-popover.popover.bottom,.iconpicker-popover.popover.bottomRightCorner,.iconpicker-popover.popover.bottomRight,.iconpicker-popover.popover.bottomLeft,.iconpicker-popover.popover.bottomLeftCorner{margin-top:10px}.iconpicker-popover.popover.left,.iconpicker-popover.popover.leftBottom,.iconpicker-popover.popover.leftTop{margin-left:-10px}.iconpicker-popover.popover.inline{margin:0 0 12px 0;position:relative;display:inline-block;opacity:1;top:auto;left:auto;bottom:auto;right:auto;max-width:100%;box-shadow:none;z-index:auto;vertical-align:top}.iconpicker-popover.popover.inline>.arrow{display:none}.dropdown-menu .iconpicker-popover.inline{margin:0;border:none}.dropdown-menu.iconpicker-container{padding:0}.iconpicker-popover.popover .popover-title{padding:12px;font-size:13px;line-height:15px;border-bottom:1px solid #ebebeb;background-color:#f7f7f7}.iconpicker-popover.popover .popover-title input[type=search].iconpicker-search{margin:0 0 2px 0}.iconpicker-popover.popover .popover-title-text~input[type=search].iconpicker-search{margin-top:12px}.iconpicker-popover.popover .popover-content{padding:0px;text-align:center}.iconpicker-popover .popover-footer{float:none;clear:both;padding:12px;text-align:right;margin:0;border-top:1px solid #ebebeb;background-color:#f7f7f7}.iconpicker-popover .popover-footer:before,.iconpicker-popover .popover-footer:after{content:" ";display:table}.iconpicker-popover .popover-footer:after{clear:both}.iconpicker-popover .popover-footer .iconpicker-btn{margin-left:10px}.iconpicker-popover .popover-footer input[type=search].iconpicker-search{margin-bottom:12px}.iconpicker-popover.popover>.arrow,.iconpicker-popover.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.iconpicker-popover.popover>.arrow{border-width:11px}.iconpicker-popover.popover>.arrow:after{border-width:10px;content:""}.iconpicker-popover.popover.top>.arrow,.iconpicker-popover.popover.topLeft>.arrow,.iconpicker-popover.popover.topRight>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.iconpicker-popover.popover.top>.arrow:after,.iconpicker-popover.popover.topLeft>.arrow:after,.iconpicker-popover.popover.topRight>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.iconpicker-popover.popover.topLeft>.arrow{left:8px;margin-left:0}.iconpicker-popover.popover.topRight>.arrow{left:auto;right:8px;margin-left:0}.iconpicker-popover.popover.right>.arrow,.iconpicker-popover.popover.rightTop>.arrow,.iconpicker-popover.popover.rightBottom>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.iconpicker-popover.popover.right>.arrow:after,.iconpicker-popover.popover.rightTop>.arrow:after,.iconpicker-popover.popover.rightBottom>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.iconpicker-popover.popover.rightTop>.arrow{top:auto;bottom:8px;margin-top:0}.iconpicker-popover.popover.rightBottom>.arrow{top:8px;margin-top:0}.iconpicker-popover.popover.bottom>.arrow,.iconpicker-popover.popover.bottomRight>.arrow,.iconpicker-popover.popover.bottomLeft>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.iconpicker-popover.popover.bottom>.arrow:after,.iconpicker-popover.popover.bottomRight>.arrow:after,.iconpicker-popover.popover.bottomLeft>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.iconpicker-popover.popover.bottomLeft>.arrow{left:8px;margin-left:0}.iconpicker-popover.popover.bottomRight>.arrow{left:auto;right:8px;margin-left:0}.iconpicker-popover.popover.left>.arrow,.iconpicker-popover.popover.leftBottom>.arrow,.iconpicker-popover.popover.leftTop>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.iconpicker-popover.popover.left>.arrow:after,.iconpicker-popover.popover.leftBottom>.arrow:after,.iconpicker-popover.popover.leftTop>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.iconpicker-popover.popover.leftBottom>.arrow{top:8px;margin-top:0}.iconpicker-popover.popover.leftTop>.arrow{top:auto;bottom:8px;margin-top:0}.iconpicker{position:relative;text-align:left;text-shadow:none;line-height:0;display:block;margin:0;overflow:hidden}.iconpicker *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;position:relative}.iconpicker:before,.iconpicker:after{content:" ";display:table}.iconpicker:after{clear:both}.iconpicker .iconpicker-items{position:relative;clear:both;float:none;padding:12px 0 0 12px;background:#fff;margin:0;overflow:hidden;overflow-y:auto;min-height:49px;max-height:160px}.iconpicker .iconpicker-items:before,.iconpicker .iconpicker-items:after{content:" ";display:table}.iconpicker .iconpicker-items:after{clear:both}.iconpicker .iconpicker-item{float:left;width:14px;height:14px;padding:12px;margin:0 12px 12px 0;text-align:center;cursor:pointer;border-radius:3px;font-size:14px;box-shadow:0 0 0 1px #ddd;color:inherit}.iconpicker .iconpicker-item:hover:not(.iconpicker-selected){background-color:#eee}.iconpicker .iconpicker-item.iconpicker-selected{box-shadow:none;color:#fff}.iconpicker-component{cursor:pointer}
{wpf-admin → admin/assets}/xml/english.xml RENAMED
@@ -546,7 +546,7 @@
546
  <phrase name="Related Topics"><![CDATA[Related Topics]]></phrase>
547
  <phrase name="Next Topic"><![CDATA[Next Topic]]></phrase>
548
  <phrase name="Previous Topic"><![CDATA[Previous Topic]]></phrase>
549
- <phrase name="All forum topics"><![CDATA[All forum topics]]></phrase>
550
  <phrase name="No tags found"><![CDATA[No tags found]]></phrase>
551
  <phrase name="Forum contains no unread posts"><![CDATA[Forum contains no unread posts]]></phrase>
552
  <phrase name="Forum contains unread posts"><![CDATA[Forum contains unread posts]]></phrase>
546
  <phrase name="Related Topics"><![CDATA[Related Topics]]></phrase>
547
  <phrase name="Next Topic"><![CDATA[Next Topic]]></phrase>
548
  <phrase name="Previous Topic"><![CDATA[Previous Topic]]></phrase>
549
+ <phrase name="All topics of this forum"><![CDATA[All topics of this forum]]></phrase>
550
  <phrase name="No tags found"><![CDATA[No tags found]]></phrase>
551
  <phrase name="Forum contains no unread posts"><![CDATA[Forum contains no unread posts]]></phrase>
552
  <phrase name="Forum contains unread posts"><![CDATA[Forum contains unread posts]]></phrase>
{wpf-admin/js/contenthover → admin/assets/xml}/index.html RENAMED
File without changes
{wpf-admin → admin/includes}/deactivation-dialog.php RENAMED
@@ -1,12 +1,12 @@
1
  <?php
2
- if (!defined('ABSPATH')) {
3
- exit();
4
  }
5
  ?>
6
  <div id="wpf_deactivation_dialog_wrap">
7
  <div id='wpf_deactivation_dialog'>
8
  <div id="wpf_deactivation_dialog_header">
9
- <h2 class="wpf-deactivation-reason-modal-title" style="float: left"><?php _e('Plugin Usage Feedback', 'wpforo'); ?></h2>
10
  <button id="wpf_deactivation_dialog_close" style="float: right">X</button>
11
  <hr style="clear: both"/>
12
  </div>
@@ -15,151 +15,151 @@ if (!defined('ABSPATH')) {
15
 
16
  <div class="wpf-deactivation-reason-desc">
17
  <p class="wpforo-desc">
18
- <strong><?php _e('Please let us know why you are deactivating. Choosing one of the options below you will help us make it better for you and for other users.', 'wpforo'); ?></strong>
19
  </p>
20
  </div>
21
  <div class="wpf-deactivation-reasons">
22
  <div class="wpf-deactivation-reason-item">
23
  <input type="radio" value="I'll reactivate it later" name="wpforo_deactivation_reason" id="wpf-reactivate_later" class="wpf-deactivation-reason"/>
24
- <label for="wpf-reactivate_later"><?php _e('I\'ll reactivate it later', 'wpforo'); ?></label>
25
  </div>
26
  <div class="wpf-deactivation-reason-item">
27
  <input type="radio" value="The plugin is not working" name="wpforo_deactivation_reason" id="wpf-not_working" class="wpf-deactivation-reason"/>
28
- <label for="wpf-not_working"><?php _e('The plugin is not working', 'wpforo'); ?></label>
29
  <div class="wpf-deactivation-reason-more-info">
30
- <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e('What kind of problems do you have?', 'wpforo'); ?>"></textarea>
31
  <div class="wpforo_deactivation_feedback">
32
- <p class="wpf-info"><?php _e('If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo'); ?></p>
33
  <div class="wpfdf_left">
34
- <label for="wpf-not_working-deactivation_feedback" class="wpf-info"><?php _e('I agree to receive email', 'wpforo'); ?>
35
  <input id="wpf-not_working-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
36
  </label>
37
  </div>
38
  <div class="wpfdf_right">
39
- <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e('email for feedback', 'wpforo'); ?>">
40
  </div>
41
  </div>
42
  </div>
43
  </div>
44
  <div class="wpf-deactivation-reason-item">
45
  <input type="radio" value="It's not what I was looking for" name="wpforo_deactivation_reason" id="wpf-not_what_i_looking_for" class="wpf-deactivation-reason"/>
46
- <label for="wpf-not_what_i_looking_for"><?php _e('It\'s not what I was looking for', 'wpforo'); ?></label>
47
  </div>
48
  <div class="wpf-deactivation-reason-item">
49
  <input type="radio" value="I couldn't understand how to make it work" name="wpforo_deactivation_reason" id="wpf-how_to_make_it_work" class="wpf-deactivation-reason"/>
50
- <label for="wpf-how_to_make_it_work"><?php _e('I couldn\'t understand how to make it work', 'wpforo'); ?></label>
51
  <div class="wpf-deactivation-reason-more-info">
52
- <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e('What type of features you want to be in the plugin?', 'wpforo'); ?>"></textarea>
53
  <div class="wpforo_deactivation_feedback">
54
- <p class="wpf-info"><?php _e('If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo'); ?></p>
55
  <div class="wpfdf_left">
56
- <label for="wpf-how_to_make_it_work-deactivation_feedback" class="wpf-info"><?php _e('I agree to receive email', 'wpforo'); ?>
57
  <input id="wpf-how_to_make_it_work-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
58
  </label>
59
  </div>
60
  <div class="wpfdf_right">
61
- <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e('email for feedback', 'wpforo'); ?>">
62
  </div>
63
  </div>
64
  </div>
65
  </div>
66
  <div class="wpf-deactivation-reason-item">
67
  <input type="radio" value="The plugin is great, but I need specific features" name="wpforo_deactivation_reason" id="wpf-need_specific_features" class="wpf-deactivation-reason"/>
68
- <label for="wpf-need_specific_features"><?php _e('The plugin is great, but I need specific features', 'wpforo'); ?></label>
69
  <div class="wpf-deactivation-reason-more-info">
70
- <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e('What type of features you want to be in the plugin?', 'wpforo'); ?>"></textarea>
71
  <div class="wpforo_deactivation_feedback">
72
- <p class="wpf-info"><?php _e('If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo'); ?></p>
73
  <div class="wpfdf_left">
74
- <label for="wpf-need_specific_features-deactivation_feedback" class="wpf-info"><?php _e('I agree to receive email', 'wpforo'); ?>
75
  <input id="wpf-need_specific_features-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
76
  </label>
77
  </div>
78
  <div class="wpfdf_right">
79
- <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e('email for feedback', 'wpforo'); ?>">
80
  </div>
81
  </div>
82
  </div>
83
  </div>
84
  <div class="wpf-deactivation-reason-item">
85
  <input type="radio" value="I didn't like plugin design" name="wpforo_deactivation_reason" id="wpf-did_not_like_design" class="wpf-deactivation-reason"/>
86
- <label for="wpf-did_not_like_design"><?php _e('I didn\'t like plugin design', 'wpforo'); ?></label>
87
  <div class="wpf-deactivation-reason-more-info">
88
- <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e('What part of design you don\'t like or want to change?', 'wpforo'); ?>"></textarea>
89
  <div class="wpforo_deactivation_feedback">
90
- <p class="wpf-info"><?php _e('If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo'); ?></p>
91
  <div class="wpfdf_left">
92
- <label for="wpf-did_not_like_design-deactivation_feedback" class="wpf-info"><?php _e('I agree to receive email', 'wpforo'); ?>
93
  <input id="wpf-did_not_like_design-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
94
  </label>
95
  </div>
96
  <div class="wpfdf_right">
97
- <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e('email for feedback', 'wpforo'); ?>">
98
  </div>
99
  </div>
100
  </div>
101
  </div>
102
  <div class="wpf-deactivation-reason-item">
103
  <input type="radio" value="The plugin works very slow" name="wpforo_deactivation_reason" id="wpf-works_very_slow" class="wpf-deactivation-reason"/>
104
- <label for="wpf-works_very_slow"><?php _e('The plugin works very slow', 'wpforo'); ?></label>
105
  <div class="wpf-deactivation-reason-more-info">
106
- <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e('Could you please describe which features of the plugin slows down your website?', 'wpforo'); ?>"></textarea>
107
  <div class="wpforo_deactivation_feedback">
108
- <p class="wpf-info"><?php _e('If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo'); ?></p>
109
  <div class="wpfdf_left">
110
- <label for="wpf-works_very_slow-deactivation_feedback" class="wpf-info"><?php _e('I agree to receive email', 'wpforo'); ?>
111
  <input id="wpf-works_very_slow-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
112
  </label>
113
  </div>
114
  <div class="wpfdf_right">
115
- <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e('email for feedback', 'wpforo'); ?>">
116
  </div>
117
  </div>
118
  </div>
119
  </div>
120
  <div class="wpf-deactivation-reason-item">
121
  <input type="radio" value="I found a better plugin" name="wpforo_deactivation_reason" id="wpf-found_better" class="wpf-deactivation-reason"/>
122
- <label for="wpf-found_better"><?php _e('I found a better plugin', 'wpforo'); ?></label>
123
  <div class="wpf-deactivation-reason-more-info">
124
- <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e('Please provide a plugin name or URL', 'wpforo'); ?>"></textarea>
125
  <div class="wpforo_deactivation_feedback">
126
- <p class="wpf-info"><?php _e('If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo'); ?></p>
127
  <div class="wpfdf_left">
128
- <label for="wpf-found_better-deactivation_feedback" class="wpf-info"><?php _e('I agree to receive email', 'wpforo'); ?>
129
  <input id="wpf-found_better-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
130
  </label>
131
  </div>
132
  <div class="wpfdf_right">
133
- <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e('email for feedback', 'wpforo'); ?>">
134
  </div>
135
  </div>
136
  </div>
137
  </div>
138
  <div class="wpf-deactivation-reason-item">
139
  <input type="radio" value="Other" name="wpforo_deactivation_reason" id="wpf-other" class="wpf-deactivation-reason"/>
140
- <label for="wpf-other"><?php _e('Other', 'wpforo'); ?></label>
141
  <div class="wpf-deactivation-reason-more-info">
142
- <textarea class="wpf_dr_more_info" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e('Please provide more information', 'wpforo'); ?>"></textarea>
143
  <div class="wpforo_deactivation_feedback">
144
- <p class="wpf-info"><?php _e('If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo'); ?></p>
145
  <div class="wpfdf_left">
146
- <label for="wpf-other-deactivation_feedback" class="wpf-info"><?php _e('I agree to receive email', 'wpforo'); ?>
147
  <input id="wpf-other-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
148
  </label>
149
  </div>
150
  <div class="wpfdf_right">
151
- <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e('email for feedback', 'wpforo'); ?>">
152
  </div>
153
  </div>
154
  </div>
155
  </div>
156
  </div>
157
  <div class="wpf-deactivation-reasons-actions">
158
- <button type="button" class="button button-secondary wpf-dismiss wpf-deactivate"><?php _e('Dismiss and never show again', 'wpforo'); ?></button>
159
- <button type="button" class="button button-primary wpf-submit wpf-deactivate"><?php _e('Submit &amp; Deactivate'); ?><i class="fas fa-pulse fa-spinner wpf-loading wpforo-hidden"></i></button>
160
  </div>
161
  </form>
162
- <h2 class="wpforo-thankyou wpforo-hidden"><?php _e('Thank you for your feedback!', 'wpforo'); ?></h2>
163
  </div>
164
  </div>
165
- </div>
1
  <?php
2
+ if( ! defined( 'ABSPATH' ) ) {
3
+ exit();
4
  }
5
  ?>
6
  <div id="wpf_deactivation_dialog_wrap">
7
  <div id='wpf_deactivation_dialog'>
8
  <div id="wpf_deactivation_dialog_header">
9
+ <h2 class="wpf-deactivation-reason-modal-title" style="float: left"><?php _e( 'Plugin Usage Feedback', 'wpforo' ); ?></h2>
10
  <button id="wpf_deactivation_dialog_close" style="float: right">X</button>
11
  <hr style="clear: both"/>
12
  </div>
15
 
16
  <div class="wpf-deactivation-reason-desc">
17
  <p class="wpforo-desc">
18
+ <strong><?php _e( 'Please let us know why you are deactivating. Choosing one of the options below you will help us make it better for you and for other users.', 'wpforo' ); ?></strong>
19
  </p>
20
  </div>
21
  <div class="wpf-deactivation-reasons">
22
  <div class="wpf-deactivation-reason-item">
23
  <input type="radio" value="I'll reactivate it later" name="wpforo_deactivation_reason" id="wpf-reactivate_later" class="wpf-deactivation-reason"/>
24
+ <label for="wpf-reactivate_later"><?php _e( 'I\'ll reactivate it later', 'wpforo' ); ?></label>
25
  </div>
26
  <div class="wpf-deactivation-reason-item">
27
  <input type="radio" value="The plugin is not working" name="wpforo_deactivation_reason" id="wpf-not_working" class="wpf-deactivation-reason"/>
28
+ <label for="wpf-not_working"><?php _e( 'The plugin is not working', 'wpforo' ); ?></label>
29
  <div class="wpf-deactivation-reason-more-info">
30
+ <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e( 'What kind of problems do you have?', 'wpforo' ); ?>"></textarea>
31
  <div class="wpforo_deactivation_feedback">
32
+ <p class="wpf-info"><?php _e( 'If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo' ); ?></p>
33
  <div class="wpfdf_left">
34
+ <label for="wpf-not_working-deactivation_feedback" class="wpf-info"><?php _e( 'I agree to receive email', 'wpforo' ); ?>
35
  <input id="wpf-not_working-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
36
  </label>
37
  </div>
38
  <div class="wpfdf_right">
39
+ <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e( 'email for feedback', 'wpforo' ); ?>">
40
  </div>
41
  </div>
42
  </div>
43
  </div>
44
  <div class="wpf-deactivation-reason-item">
45
  <input type="radio" value="It's not what I was looking for" name="wpforo_deactivation_reason" id="wpf-not_what_i_looking_for" class="wpf-deactivation-reason"/>
46
+ <label for="wpf-not_what_i_looking_for"><?php _e( 'It\'s not what I was looking for', 'wpforo' ); ?></label>
47
  </div>
48
  <div class="wpf-deactivation-reason-item">
49
  <input type="radio" value="I couldn't understand how to make it work" name="wpforo_deactivation_reason" id="wpf-how_to_make_it_work" class="wpf-deactivation-reason"/>
50
+ <label for="wpf-how_to_make_it_work"><?php _e( 'I couldn\'t understand how to make it work', 'wpforo' ); ?></label>
51
  <div class="wpf-deactivation-reason-more-info">
52
+ <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e( 'What type of features you want to be in the plugin?', 'wpforo' ); ?>"></textarea>
53
  <div class="wpforo_deactivation_feedback">
54
+ <p class="wpf-info"><?php _e( 'If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo' ); ?></p>
55
  <div class="wpfdf_left">
56
+ <label for="wpf-how_to_make_it_work-deactivation_feedback" class="wpf-info"><?php _e( 'I agree to receive email', 'wpforo' ); ?>
57
  <input id="wpf-how_to_make_it_work-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
58
  </label>
59
  </div>
60
  <div class="wpfdf_right">
61
+ <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e( 'email for feedback', 'wpforo' ); ?>">
62
  </div>
63
  </div>
64
  </div>
65
  </div>
66
  <div class="wpf-deactivation-reason-item">
67
  <input type="radio" value="The plugin is great, but I need specific features" name="wpforo_deactivation_reason" id="wpf-need_specific_features" class="wpf-deactivation-reason"/>
68
+ <label for="wpf-need_specific_features"><?php _e( 'The plugin is great, but I need specific features', 'wpforo' ); ?></label>
69
  <div class="wpf-deactivation-reason-more-info">
70
+ <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e( 'What type of features you want to be in the plugin?', 'wpforo' ); ?>"></textarea>
71
  <div class="wpforo_deactivation_feedback">
72
+ <p class="wpf-info"><?php _e( 'If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo' ); ?></p>
73
  <div class="wpfdf_left">
74
+ <label for="wpf-need_specific_features-deactivation_feedback" class="wpf-info"><?php _e( 'I agree to receive email', 'wpforo' ); ?>
75
  <input id="wpf-need_specific_features-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
76
  </label>
77
  </div>
78
  <div class="wpfdf_right">
79
+ <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e( 'email for feedback', 'wpforo' ); ?>">
80
  </div>
81
  </div>
82
  </div>
83
  </div>
84
  <div class="wpf-deactivation-reason-item">
85
  <input type="radio" value="I didn't like plugin design" name="wpforo_deactivation_reason" id="wpf-did_not_like_design" class="wpf-deactivation-reason"/>
86
+ <label for="wpf-did_not_like_design"><?php _e( 'I didn\'t like plugin design', 'wpforo' ); ?></label>
87
  <div class="wpf-deactivation-reason-more-info">
88
+ <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e( 'What part of design you don\'t like or want to change?', 'wpforo' ); ?>"></textarea>
89
  <div class="wpforo_deactivation_feedback">
90
+ <p class="wpf-info"><?php _e( 'If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo' ); ?></p>
91
  <div class="wpfdf_left">
92
+ <label for="wpf-did_not_like_design-deactivation_feedback" class="wpf-info"><?php _e( 'I agree to receive email', 'wpforo' ); ?>
93
  <input id="wpf-did_not_like_design-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
94
  </label>
95
  </div>
96
  <div class="wpfdf_right">
97
+ <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e( 'email for feedback', 'wpforo' ); ?>">
98
  </div>
99
  </div>
100
  </div>
101
  </div>
102
  <div class="wpf-deactivation-reason-item">
103
  <input type="radio" value="The plugin works very slow" name="wpforo_deactivation_reason" id="wpf-works_very_slow" class="wpf-deactivation-reason"/>
104
+ <label for="wpf-works_very_slow"><?php _e( 'The plugin works very slow', 'wpforo' ); ?></label>
105
  <div class="wpf-deactivation-reason-more-info">
106
+ <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e( 'Could you please describe which features of the plugin slows down your website?', 'wpforo' ); ?>"></textarea>
107
  <div class="wpforo_deactivation_feedback">
108
+ <p class="wpf-info"><?php _e( 'If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo' ); ?></p>
109
  <div class="wpfdf_left">
110
+ <label for="wpf-works_very_slow-deactivation_feedback" class="wpf-info"><?php _e( 'I agree to receive email', 'wpforo' ); ?>
111
  <input id="wpf-works_very_slow-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
112
  </label>
113
  </div>
114
  <div class="wpfdf_right">
115
+ <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e( 'email for feedback', 'wpforo' ); ?>">
116
  </div>
117
  </div>
118
  </div>
119
  </div>
120
  <div class="wpf-deactivation-reason-item">
121
  <input type="radio" value="I found a better plugin" name="wpforo_deactivation_reason" id="wpf-found_better" class="wpf-deactivation-reason"/>
122
+ <label for="wpf-found_better"><?php _e( 'I found a better plugin', 'wpforo' ); ?></label>
123
  <div class="wpf-deactivation-reason-more-info">
124
+ <textarea class="wpf_dr_more_info" required="required" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e( 'Please provide a plugin name or URL', 'wpforo' ); ?>"></textarea>
125
  <div class="wpforo_deactivation_feedback">
126
+ <p class="wpf-info"><?php _e( 'If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo' ); ?></p>
127
  <div class="wpfdf_left">
128
+ <label for="wpf-found_better-deactivation_feedback" class="wpf-info"><?php _e( 'I agree to receive email', 'wpforo' ); ?>
129
  <input id="wpf-found_better-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
130
  </label>
131
  </div>
132
  <div class="wpfdf_right">
133
+ <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e( 'email for feedback', 'wpforo' ); ?>">
134
  </div>
135
  </div>
136
  </div>
137
  </div>
138
  <div class="wpf-deactivation-reason-item">
139
  <input type="radio" value="Other" name="wpforo_deactivation_reason" id="wpf-other" class="wpf-deactivation-reason"/>
140
+ <label for="wpf-other"><?php _e( 'Other', 'wpforo' ); ?></label>
141
  <div class="wpf-deactivation-reason-more-info">
142
+ <textarea class="wpf_dr_more_info" name="wpforo_deactivation_reason_desc" rows="3" placeholder="<?php _e( 'Please provide more information', 'wpforo' ); ?>"></textarea>
143
  <div class="wpforo_deactivation_feedback">
144
+ <p class="wpf-info"><?php _e( 'If you want us to contact you please click on "I agree to receive email" checkbox, then fill out your email. We\'ll try to do our best to help you with problems.', 'wpforo' ); ?></p>
145
  <div class="wpfdf_left">
146
+ <label for="wpf-other-deactivation_feedback" class="wpf-info"><?php _e( 'I agree to receive email', 'wpforo' ); ?>
147
  <input id="wpf-other-deactivation_feedback" type="checkbox" name="wpforo_deactivation_feedback">
148
  </label>
149
  </div>
150
  <div class="wpfdf_right">
151
+ <input type="email" name="wpforo_deactivation_feedback_email" autocomplete="off" placeholder="<?php _e( 'email for feedback', 'wpforo' ); ?>">
152
  </div>
153
  </div>
154
  </div>
155
  </div>
156
  </div>
157
  <div class="wpf-deactivation-reasons-actions">
158
+ <button type="button" class="button button-secondary wpf-dismiss wpf-deactivate"><?php _e( 'Dismiss and never show again', 'wpforo' ); ?></button>
159
+ <button type="button" class="button button-primary wpf-submit wpf-deactivate"><?php _e( 'Submit &amp; Deactivate' ); ?><i class="fas fa-pulse fa-spinner wpf-loading wpforo-hidden"></i></button>
160
  </div>
161
  </form>
162
+ <h2 class="wpforo-thankyou wpforo-hidden"><?php _e( 'Thank you for your feedback!', 'wpforo' ); ?></h2>
163
  </div>
164
  </div>
165
+ </div>
{wpf-admin/js → admin/includes}/index.html RENAMED
File without changes
admin/index.php ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ add_action( 'admin_menu', function() {
7
+ if( is_wpforo_multiboard() ){
8
+ wpforo_admin_menu();
9
+ }else{
10
+ wpforo_admin_menu_mono();
11
+ }
12
+ }, 39 );
13
+
14
+ function wpforo_admin_menu_mono(){
15
+ if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'aum' ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
16
+ $boards = WPF()->board->get_boards( [ 'status' => true ] );
17
+ $menu_position = apply_filters( 'wpforo_admin_menu_position', 23 );
18
+ $parent_slug = 'wpforo-overview';
19
+ $attention_count = WPF()->member->get_count( [ 'p.status' => ['banned','inactive'] ] );
20
+ $after_members_menu_title = $attention_count ? ' <span class="awaiting-mod count-1" title="'. __( 'Inactive and banned', 'wpforo' ) .'"><span class="pending-count" style="color:#ffffff;">' . $attention_count . '</span></span> ' : '';
21
+ add_menu_page(
22
+ 'wpForo',
23
+ 'wpForo',
24
+ 'read',
25
+ $parent_slug,
26
+ function() {
27
+ require( WPFORO_DIR . '/admin/pages/overview.php' );
28
+ require WPFORO_DIR . '/admin/pages/dashboard.php';
29
+ },
30
+ 'dashicons-format-chat',
31
+ $menu_position
32
+ );
33
+ add_submenu_page(
34
+ $parent_slug,
35
+ __( 'Overview', 'wpforo' ),
36
+ __( 'Overview', 'wpforo' ),
37
+ 'read',
38
+ $parent_slug
39
+ );
40
+
41
+ foreach( $boards as $board ) {
42
+ WPF()->change_board( $board['boardid'] );
43
+
44
+ if( wpforo_setting( 'antispam', 'spam_file_scanner' ) ) WPF()->moderation->spam_attachment();
45
+
46
+ $all_count = 0;
47
+ $mod_count = WPF()->post->unapproved_count();
48
+ $all_count += $mod_count;
49
+ $mod_count = $mod_count ? ' <span class="awaiting-mod count-1"><span class="pending-count">' . $mod_count . '</span></span> ' : '';
50
+ $all_count = $all_count ? ' <span class="awaiting-mod count-1"><span class="pending-count">' . $all_count . '</span></span> ' : '';
51
+
52
+ if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'mt' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'mth' ) || WPF()->usergroup->can( 'aum' ) ) {
53
+ if( WPF()->usergroup->can( 'mf' ) || wpforo_current_user_is( 'admin' ) ) {
54
+ add_submenu_page( $parent_slug, __( 'Forums', 'wpforo' ), __( 'Forums', 'wpforo' ), 'read', wpforo_prefix_slug( 'forums' ), function() {
55
+ require( WPFORO_DIR . '/admin/pages/forum.php' );
56
+ } );
57
+ }
58
+ if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
59
+ add_submenu_page( $parent_slug, __( 'Settings', 'wpforo' ), __( 'Settings', 'wpforo' ), 'read', wpforo_prefix_slug( 'settings' ), function() {
60
+ require( WPFORO_DIR . '/admin/pages/settings.php' );
61
+ } );
62
+ }
63
+ if( WPF()->usergroup->can( 'aum' ) || wpforo_current_user_is( 'admin' ) ) {
64
+ add_submenu_page( $parent_slug, __( 'Moderation', 'wpforo' ), __( 'Moderation', 'wpforo' ) . $mod_count, 'read', wpforo_prefix_slug( 'moderations' ), function() {
65
+ require( WPFORO_DIR . '/admin/pages/moderation.php' );
66
+ } );
67
+ }
68
+ if( WPF()->usergroup->can( 'mp' ) || wpforo_current_user_is( 'admin' ) ) {
69
+ add_submenu_page( $parent_slug, __( 'Phrases', 'wpforo' ), __( 'Phrases', 'wpforo' ), 'read', wpforo_prefix_slug( 'phrases' ), function() {
70
+ require( WPFORO_DIR . '/admin/pages/phrase.php' );
71
+ } );
72
+ }
73
+ if( WPF()->usergroup->can( 'mt' ) || wpforo_current_user_is( 'admin' ) ) {
74
+ add_submenu_page( $parent_slug, __( 'Tools', 'wpforo' ), __( 'Tools', 'wpforo' ), 'read', wpforo_prefix_slug( 'tools' ), function() {
75
+ require( WPFORO_DIR . '/admin/pages/tools.php' );
76
+ } );
77
+ }
78
+
79
+ do_action( 'wpforo_admin_menu', $parent_slug, $board );
80
+ }
81
+ }
82
+
83
+ add_submenu_page( $parent_slug, __( 'Boards', 'wpforo' ), __( 'Boards', 'wpforo' ), 'read', 'wpforo-boards', function() {
84
+ require( WPFORO_DIR . '/admin/pages/board.php' );
85
+ } );
86
+ if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
87
+ add_submenu_page( $parent_slug, __( 'Accesses', 'wpforo' ), __( 'Accesses', 'wpforo' ), 'read', 'wpforo-accesses', function() {
88
+ require( WPFORO_DIR . '/admin/pages/accesses.php' );
89
+ } );
90
+ }
91
+ add_submenu_page( $parent_slug, __( 'Usergroups', 'wpforo' ), __( 'Usergroups', 'wpforo' ), 'read', 'wpforo-usergroups', function() {
92
+ require( WPFORO_DIR . '/admin/pages/usergroup.php' );
93
+ } );
94
+ add_submenu_page( $parent_slug, __( 'Members', 'wpforo' ), __( 'Members', 'wpforo' ) . $after_members_menu_title, 'read', 'wpforo-members', function() {
95
+ require( WPFORO_DIR . '/admin/pages/member.php' );
96
+ } );
97
+ if( WPF()->usergroup->can( 'mth' ) || wpforo_current_user_is( 'admin' ) ) {
98
+ add_submenu_page( $parent_slug, __( 'Themes', 'wpforo' ), __( 'Themes', 'wpforo' ), 'read', wpforo_prefix_slug( 'themes' ), function() {
99
+ require( WPFORO_DIR . '/admin/pages/themes.php' );
100
+ } );
101
+ }
102
+ if( wpforo_current_user_is( 'admin' ) ) {
103
+ add_submenu_page( $parent_slug, __( 'Addons', 'wpforo' ), __( 'Addons', 'wpforo' ), 'read', wpforo_prefix_slug( 'addons' ), function() {
104
+ require( WPFORO_DIR . '/admin/pages/addons.php' );
105
+ } );
106
+ }
107
+
108
+ do_action( 'wpforo_admin_base_menu', $parent_slug );
109
+ }
110
+ }
111
+
112
+ function wpforo_admin_menu(){
113
+ if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'vm' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'aum' ) || WPF()->usergroup->can( 'vmg' ) || WPF()->usergroup->can( 'mth' ) ) {
114
+ $menu_position = apply_filters( 'wpforo_admin_menu_position', 23 );
115
+ $parent_slug = 'wpforo-overview';
116
+ $attention_count = WPF()->member->get_count( [ 'p.status' => ['banned', 'inactive'] ] );
117
+ $after_members_menu_title = $attention_count ? ' <span class="awaiting-mod count-1" title="'. __( 'Inactive and Banned', 'wpforo' ) .'"><span class="pending-count" style="color:#ffffff;">' . $attention_count . '</span></span> ' : '';
118
+ add_menu_page(
119
+ 'wpForo',
120
+ 'wpForo',
121
+ 'read',
122
+ $parent_slug,
123
+ function() {
124
+ require( WPFORO_DIR . '/admin/pages/overview.php' );
125
+ },
126
+ 'dashicons-format-chat',
127
+ $menu_position
128
+ );
129
+ add_submenu_page(
130
+ $parent_slug,
131
+ __( 'Overview', 'wpforo' ),
132
+ __( 'Overview', 'wpforo' ),
133
+ 'read',
134
+ $parent_slug
135
+ );
136
+ add_submenu_page( $parent_slug, __( 'Boards', 'wpforo' ), __( 'Boards', 'wpforo' ), 'read', 'wpforo-boards', function() {
137
+ require( WPFORO_DIR . '/admin/pages/board.php' );
138
+ } );
139
+ if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
140
+ add_submenu_page( $parent_slug, __( 'Accesses', 'wpforo' ), __( 'Accesses', 'wpforo' ), 'read', 'wpforo-accesses', function() {
141
+ require( WPFORO_DIR . '/admin/pages/accesses.php' );
142
+ } );
143
+ }
144
+ add_submenu_page( $parent_slug, __( 'Usergroups', 'wpforo' ), __( 'Usergroups', 'wpforo' ), 'read', 'wpforo-usergroups', function() {
145
+ require( WPFORO_DIR . '/admin/pages/usergroup.php' );
146
+ } );
147
+ add_submenu_page( $parent_slug, __( 'Members', 'wpforo' ), __( 'Members', 'wpforo' ) . $after_members_menu_title, 'read', 'wpforo-members', function() {
148
+ require( WPFORO_DIR . '/admin/pages/member.php' );
149
+ } );
150
+ if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
151
+ add_submenu_page( $parent_slug, __( 'Settings', 'wpforo' ), __( 'Settings', 'wpforo' ), 'read', 'wpforo-base-settings', function() {
152
+ require( WPFORO_DIR . '/admin/pages/settings.php' );
153
+ } );
154
+ }
155
+ if( WPF()->usergroup->can( 'mth' ) || wpforo_current_user_is( 'admin' ) ) {
156
+ add_submenu_page( $parent_slug, __( 'Themes', 'wpforo' ), __( 'Themes', 'wpforo' ), 'read', wpforo_prefix_slug( 'themes' ), function() {
157
+ require( WPFORO_DIR . '/admin/pages/themes.php' );
158
+ } );
159
+ }
160
+ if( wpforo_current_user_is( 'admin' ) ) {
161
+ add_submenu_page( $parent_slug, __( 'Addons', 'wpforo' ), __( 'Addons', 'wpforo' ), 'read', wpforo_prefix_slug( 'addons' ), function() {
162
+ require( WPFORO_DIR . '/admin/pages/addons.php' );
163
+ } );
164
+ }
165
+
166
+ do_action( 'wpforo_admin_base_menu', $parent_slug );
167
+
168
+ $boards = WPF()->board->get_boards( [ 'status' => true ] );
169
+ foreach( $boards as $board ) {
170
+ WPF()->change_board( [ 'boardid' => $board['boardid'] ] );
171
+
172
+ if( wpforo_setting( 'antispam', 'spam_file_scanner' ) ) WPF()->moderation->spam_attachment();
173
+
174
+ $all_count = 0;
175
+ $mod_count = WPF()->post->unapproved_count();
176
+ $all_count += $mod_count;
177
+ $mod_count = $mod_count ? ' <span class="awaiting-mod count-1"><span class="pending-count">' . $mod_count . '</span></span> ' : '';
178
+ $all_count = $all_count ? ' <span class="awaiting-mod count-1"><span class="pending-count">' . $all_count . '</span></span> ' : '';
179
+
180
+ if( wpforo_current_user_is( 'admin' ) || WPF()->usergroup->can( 'mf' ) || WPF()->usergroup->can( 'ms' ) || WPF()->usergroup->can( 'mt' ) || WPF()->usergroup->can( 'mp' ) || WPF()->usergroup->can( 'mth' ) || WPF()->usergroup->can( 'aum' ) ) {
181
+ $parent_slug = wpforo_prefix_slug( 'dashboard' );
182
+ add_menu_page(
183
+ __( 'Dashboard', 'wpforo' ),
184
+ $board['title'] . $all_count,
185
+ 'read',
186
+ $parent_slug,
187
+ function() {
188
+ require( WPFORO_DIR . '/admin/pages/dashboard.php' );
189
+ },
190
+ 'dashicons-format-chat',
191
+ $menu_position
192
+ );
193
+ add_submenu_page(
194
+ $parent_slug,
195
+ __( 'Dashboard', 'wpforo' ),
196
+ __( 'Dashboard', 'wpforo' ),
197
+ 'read',
198
+ $parent_slug
199
+ );
200
+ if( WPF()->usergroup->can( 'mf' ) || wpforo_current_user_is( 'admin' ) ) {
201
+ add_submenu_page( $parent_slug, __( 'Forums', 'wpforo' ), __( 'Forums', 'wpforo' ), 'read', wpforo_prefix_slug( 'forums' ), function() {
202
+ require( WPFORO_DIR . '/admin/pages/forum.php' );
203
+ } );
204
+ }
205
+ if( WPF()->usergroup->can( 'ms' ) || wpforo_current_user_is( 'admin' ) ) {
206
+ add_submenu_page( $parent_slug, __( 'Settings', 'wpforo' ), __( 'Settings', 'wpforo' ), 'read', wpforo_prefix_slug( 'settings' ), function() {
207
+ require( WPFORO_DIR . '/admin/pages/settings.php' );
208
+ } );
209
+ }
210
+ if( WPF()->usergroup->can( 'aum' ) || wpforo_current_user_is( 'admin' ) ) {
211
+ add_submenu_page( $parent_slug, __( 'Moderation', 'wpforo' ), __( 'Moderation', 'wpforo' ) . $mod_count, 'read', wpforo_prefix_slug( 'moderations' ), function() {
212
+ require( WPFORO_DIR . '/admin/pages/moderation.php' );
213
+ } );
214
+ }
215
+ if( WPF()->usergroup->can( 'mp' ) || wpforo_current_user_is( 'admin' ) ) {
216
+ add_submenu_page( $parent_slug, __( 'Phrases', 'wpforo' ), __( 'Phrases', 'wpforo' ), 'read', wpforo_prefix_slug( 'phrases' ), function() {
217
+ require( WPFORO_DIR . '/admin/pages/phrase.php' );
218
+ } );
219
+ }
220
+ if( WPF()->usergroup->can( 'mt' ) || wpforo_current_user_is( 'admin' ) ) {
221
+ add_submenu_page( $parent_slug, __( 'Tools', 'wpforo' ), __( 'Tools', 'wpforo' ), 'read', wpforo_prefix_slug( 'tools' ), function() {
222
+ require( WPFORO_DIR . '/admin/pages/tools.php' );
223
+ } );
224
+ }
225
+
226
+ do_action( 'wpforo_admin_menu', $parent_slug, $board );
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ add_action( 'admin_footer', function() {
233
+ if( ! wpforo_get_option( 'deactivation_dialog_never_show', false, false ) && ( strpos( wpforo_get_request_uri(), '/plugins.php' ) !== false ) ) {
234
+ require( WPFORO_DIR . '/admin/includes/deactivation-dialog.php' );
235
+ }
236
+ } );
237
+
238
+ add_action( 'admin_footer', function() {
239
+ $display = ( apply_filters( 'wpforo_admin_loading', false ) ) ? 'block' : 'none';
240
+ echo '<div id="wpf-admin-loading-extrawrap" style="display: ' . $display . '">
241
+ <div class="wpf-admin-loading-wrap">
242
+ <div class="wpf-admin-loading-ico dashicons dashicons-update"></div>
243
+ <div class="wpf-admin-loading-txt">' . __( 'don\'t stop or close browser', 'wpforo' ) . '</div>
244
+ </div>
245
+ </div>';
246
+ } );
admin/listtables/Boards.php ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\admin\listtables;
4
+
5
+ use WP_List_Table;
6
+
7
+ require_once( ABSPATH . 'wp-admin/includes/template.php' );
8
+ require_once( ABSPATH . 'wp-admin/includes/screen.php' );
9
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-screen.php' );
10
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
11
+
12
+ class Boards extends WP_List_Table {
13
+ /** ************************************************************************
14
+ * REQUIRED. Set up a constructor that references the parent constructor. We
15
+ * use the parent reference to set some default configs.
16
+ ***************************************************************************/
17
+ function __construct() {
18
+ //Set parent defaults
19
+ parent::__construct( [
20
+ 'singular' => 'board', //singular name of the listed records
21
+ 'plural' => 'boards', //plural name of the listed records
22
+ 'ajax' => false, //does this table support ajax?
23
+ 'screen' => 'wpForoBoards',
24
+ ] );
25
+
26
+ }
27
+
28
+ function column_default( $item, $column_name ) {
29
+ return $item[ $column_name ];
30
+ }
31
+
32
+ function column_pageid( $item ) {
33
+ return ( ( $edit_post_link = get_edit_post_link( $item['pageid'] ) ) ? '<a href="' . $edit_post_link . '">' . $item['pageid'] . '</a>' : '<span style="color: red">' . $item['pageid'] . ' ' . __( 'not found', 'wpforo' ) . '</span>' );
34
+ }
35
+
36
+ function column_status( $item ) {
37
+ return ( $item['status'] ? '<span style="color: green">' . __( 'YES', 'wpforo' ) . '</span>' : '<span style="color: red">' . __( 'NO', 'wpforo' ) . '</span>' );
38
+ }
39
+
40
+ function column_is_standalone( $item ) {
41
+ return ( $item['is_standalone'] ? '<span style="color: green">' . __( 'YES', 'wpforo' ) . '</span>' : '<span style="color: red">' . __( 'NO', 'wpforo' ) . '</span>' );
42
+ }
43
+
44
+ /** ************************************************************************
45
+ * Recommended. This is a custom column method and is responsible for what
46
+ * is rendered in any column with a name/slug of 'title'. Every time the class
47
+ * needs to render a column, it first looks for a method named
48
+ * column_{$column_title} - if it exists, that method is run. If it doesn't
49
+ * exist, column_default() is called instead.
50
+ *
51
+ * This example also illustrates how to implement rollover actions. Actions
52
+ * should be an associative array formatted as 'slug'=>'link html' - and you
53
+ * will need to generate the URLs yourself. You could even ensure the links
54
+ *
55
+ *
56
+ * @param array $item A singular item (one full row's worth of data)
57
+ *
58
+ * @return string Text to be placed inside the column <td> (movie title only)
59
+ **************************************************************************@see WP_List_Table::::single_row_columns()
60
+ */
61
+ function column_boardid( $item ) {
62
+ $ehref = admin_url(
63
+ sprintf(
64
+ 'admin.php?page=%1$s&wpfaction=%2$s&boardid=%3$s',
65
+ 'wpforo-boards',
66
+ 'wpforo_board_save_form',
67
+ $item['boardid']
68
+ )
69
+ );
70
+ $vhref = wpforo_url( '', $item['slug'] );
71
+ $shref = rtrim( wpforo_url( 'sitemap.xml', $item['slug'] ), '/' );
72
+ $dhref = wp_nonce_url(
73
+ admin_url(
74
+ sprintf(
75
+ 'admin.php?page=%1$s&wpfaction=%2$s&boardid=%3$s',
76
+ 'wpforo-boards',
77
+ 'board_delete',
78
+ $item['boardid']
79
+ )
80
+ ),
81
+ 'wpforo-board-delete-' . $item['boardid']
82
+ );
83
+ //Build row actions
84
+ $actions = [
85
+ 'edit' => '<a href="' . $ehref . '">' . __( 'Edit', 'wpforo' ) . '</a>',
86
+ 'view' => '<a href="' . $vhref . '" target="_blank">' . __( 'View', 'wpforo' ) . '</a>',
87
+ 'sitemap' => '<a href="' . $shref . '" target="_blank">' . __( 'Sitemap', 'wpforo' ) . '</a>',
88
+ 'delete' => '<a onclick="return confirm(\'' . __( "Are you sure you want to DELETE this board and his data permanently?", 'wpforo' ) . '\');" href="' . $dhref . '">' . __( 'Delete', 'wpforo' ) . '</a>',
89
+ ];
90
+ if( ! $item['boardid'] ) unset( $actions['delete'] );
91
+ if( ! $item['status'] ) unset( $actions['view'] );
92
+
93
+ //Return the title contents
94
+ return sprintf(
95
+ '%1$s %2$s',
96
+ /*$1%s*/ $item['boardid'],
97
+ /*$2%s*/ $this->row_actions( $actions )
98
+ );
99
+ }
100
+
101
+
102
+ /** ************************************************************************
103
+ * REQUIRED! This method dictates the table's columns and titles. This should
104
+ * return an array where the key is the column slug (and class) and the value
105
+ * is the column's title text. If you need a checkbox for bulk actions, refer
106
+ * to the $columns array below.
107
+ *
108
+ * The 'cb' column is treated differently than the rest. If including a checkbox
109
+ * column in your table you must create a column_cb() method. If you don't need
110
+ * to bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
111
+ *
112
+ * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
113
+ **************************************************************************@see WP_List_Table::::single_row_columns()
114
+ */
115
+ function get_columns() {
116
+ return [
117
+ 'boardid' => __( 'boardid', 'wpforo' ),
118
+ 'title' => __( 'title', 'wpforo' ),
119
+ 'slug' => __( 'slug', 'wpforo' ),
120
+ 'pageid' => __( 'pageid', 'wpforo' ),
121
+ 'locale' => __( 'locale', 'wpforo' ),
122
+ 'is_standalone' => __( 'is standalone', 'wpforo' ),
123
+ 'status' => __( 'enabled', 'wpforo' ),
124
+ ];
125
+ }
126
+
127
+ /** ************************************************************************
128
+ * REQUIRED! This is where you prepare your data for display. This method will
129
+ * usually be used to query the database, sort and filter the data, and generally
130
+ * get it ready to be displayed. At a minimum, we should set $this->items and
131
+ * $this->set_pagination_args(), although the following properties and methods
132
+ * are frequently interacted with here...
133
+ *
134
+ * @uses $this->_column_headers
135
+ * @uses $this->items
136
+ * @uses $this->get_columns()
137
+ * @uses $this->get_sortable_columns()
138
+ * @uses $this->get_pagenum()
139
+ * @uses $this->set_pagination_args()
140
+ **************************************************************************/
141
+ function prepare_items() {
142
+ /**
143
+ * REQUIRED. Now we need to define our column headers. This includes a complete
144
+ * array of columns to be displayed (slugs & titles), a list of columns
145
+ * to keep hidden, and a list of columns that are sortable. Each of these
146
+ * can be defined in another method (as we've done here) before being
147
+ * used to build the value for our _column_headers property.
148
+ */
149
+ $columns = $this->get_columns();
150
+ $hidden = [];
151
+ $sortable = $this->get_sortable_columns();
152
+
153
+
154
+ /**
155
+ * REQUIRED. Finally, we build an array to be used by the class for column
156
+ * headers. The $this->_column_headers property takes an array which contains
157
+ * 3 other arrays. One for all columns, one for hidden columns, and one
158
+ * for sortable columns.
159
+ */
160
+ $this->_column_headers = [ $columns, $hidden, $sortable ];
161
+
162
+ /**
163
+ * REQUIRED. Now we can add our *sorted* data to the items' property, where
164
+ * it can be used by the rest of the class.
165
+ */
166
+ $this->items = WPF()->board->get_boards();
167
+ }
168
+ }
admin/listtables/Members.php ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\admin\listtables;
4
+
5
+ use WP_List_Table;
6
+
7
+ require_once( ABSPATH . 'wp-admin/includes/template.php' );
8
+ require_once( ABSPATH . 'wp-admin/includes/screen.php' );
9
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-screen.php' );
10
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
11
+
12
+ class Members extends WP_List_Table {
13
+
14
+ public $wpfitems_count;
15
+
16
+ /** ************************************************************************
17
+ * REQUIRED. Set up a constructor that references the parent constructor. We
18
+ * use the parent reference to set some default configs.
19
+ ***************************************************************************/
20
+ function __construct() {
21
+ //Set parent defaults
22
+ parent::__construct( [
23
+ 'singular' => 'member', //singular name of the listed records
24
+ 'plural' => 'members', //plural name of the listed records
25
+ 'ajax' => false, //does this table support ajax?
26
+ 'screen' => 'wpForoMembers',
27
+ ] );
28
+
29
+ }
30
+
31
+
32
+ /** ************************************************************************
33
+ * Recommended. This method is called when the parent class can't find a method
34
+ * specifically build for a given column. Generally, it's recommended to include
35
+ * one method for each column you want to render, keeping your package class
36
+ * neat and organized. For example, if the class needs to process a column
37
+ * named 'title', it would first see if a method named $this->column_title()
38
+ * exists - if it does, that method will be used. If it doesn't, this one will
39
+ * be used. Generally, you should try to use custom column methods as much as
40
+ * possible.
41
+ *
42
+ * Since we have defined a column_title() method later on, this method doesn't
43
+ * need to concern itself with any column with a name of 'title'. Instead, it
44
+ * needs to handle everything else.
45
+ *
46
+ * For more detailed insight into how columns are handled, take a look at
47
+ * WP_List_Table::single_row_columns()
48
+ *
49
+ * @param array $item A singular item (one full row's worth of data)
50
+ * @param string $column_name The name/slug of the column to be processed
51
+ *
52
+ * @return string Text or HTML to be placed inside the column <td>
53
+ **************************************************************************/
54
+ function column_default( $item, $column_name ) {
55
+ switch( $column_name ) {
56
+ case 'groupid':
57
+ $usergroup = WPF()->usergroup->get_usergroup( $item[ $column_name ] );
58
+
59
+ return ! empty( $usergroup['name'] ) ? esc_html( $usergroup['name'] ) : __( 'default', 'wpforo' );
60
+ case 'blog_posts':
61
+ return WPF()->member->blog_posts( $item['userid'] );
62
+ case 'blog_comments':
63
+ return WPF()->member->blog_comments( $item['userid'], $item['user_email'] );
64
+ case 'online_time':
65
+ return ( $item['online_time'] ) ? wpforo_date_raw( $item['online_time'], 'datetime', false ) : '-';
66
+ default:
67
+ return $item[ $column_name ];
68
+ }
69
+ }
70
+
71
+
72
+ /** ************************************************************************
73
+ * Recommended. This is a custom column method and is responsible for what
74
+ * is rendered in any column with a name/slug of 'title'. Every time the class
75
+ * needs to render a column, it first looks for a method named
76
+ * column_{$column_title} - if it exists, that method is run. If it doesn't
77
+ * exist, column_default() is called instead.
78
+ *
79
+ * This example also illustrates how to implement rollover actions. Actions
80
+ * should be an associative array formatted as 'slug'=>'link html' - and you
81
+ * will need to generate the URLs yourself. You could even ensure the links
82
+ *
83
+ *
84
+ * @param array $item A singular item (one full row's worth of data)
85
+ *
86
+ * @return string Text to be placed inside the column <td> (movie title only)
87
+ **************************************************************************@see WP_List_Table::::single_row_columns()
88
+ */
89
+ function column_userid( $item ) {
90
+ $edit_user = admin_url( 'user-edit.php?user_id=' . intval( $item['userid'] ) );
91
+ $edit_profile = WPF()->member->get_profile_url( $item['userid'], 'account' );
92
+ $ban = wp_nonce_url( admin_url( sprintf( 'admin.php?page=wpforo-members&wpfaction=%1$s&userid=%2$s', 'user_ban', $item['userid'] ) ), 'wpforo-user-ban-' . $item['userid'] );
93
+ $unban = wp_nonce_url( admin_url( sprintf( 'admin.php?page=wpforo-members&wpfaction=%1$s&userid=%2$s', 'user_unban', $item['userid'] ) ), 'wpforo-user-unban-' . $item['userid'] );
94
+ $activate = wp_nonce_url( admin_url( sprintf( 'admin.php?page=wpforo-members&wpfaction=%1$s&userid=%2$s', 'user_activate', $item['userid'] ) ), 'wpforo-user-activate-' . $item['userid'] );
95
+ $deactivate = wp_nonce_url( admin_url( sprintf( 'admin.php?page=wpforo-members&wpfaction=%1$s&userid=%2$s', 'user_deactivate', $item['userid'] ) ), 'wpforo-user-deactivate-' . $item['userid'] );
96
+ $delete = wp_nonce_url( "users.php?action=delete&user=" . intval( $item['userid'] ), 'bulk-users' );
97
+ $actions = [
98
+ 'edit_user' => '<a href="' . esc_url( $edit_user ) . '">' . __( 'Edit User', 'wpforo' ) . '</a>',
99
+ 'edit_profile' => '<a href="' . esc_url( $edit_profile ) . '">' . __( 'Edit Profile', 'wpforo' ) . '</a>',
100
+ 'ban' => '<a href="' . esc_url( $ban ) . '" style="color: orange;" title="' . __( 'Ban', 'wpforo' ) . '" onclick="return confirm(\'' . __( 'Are you sure, you want to BAN this user?', 'wpforo' ) . '\')">' . __( 'Ban', 'wpforo' ) . '</a>',
101
+ 'unban' => '<a href="' . esc_url( $unban ) . '" style="color: orange;" title="' . __( 'Unban', 'wpforo' ) . '" onclick="return confirm(\'' . __( 'Are you sure, you want to UNBAN this user?', 'wpforo' ) . '\')">' . __( 'Unban', 'wpforo' ) . '</a>',
102
+ 'activate' => '<a href="' . esc_url( $activate ) . '" style="color: green;" title="' . __( 'Activate', 'wpforo' ) . '" onclick="return confirm(\'' . __( 'Are you sure, you want to activate this user?', 'wpforo' ) . '\')">' . __( 'Activate', 'wpforo' ) . '</a>',
103
+ 'deactivate' => '<a href="' . esc_url( $deactivate ) . '" style="color: #8a5700;" title="' . __( 'Deactivate', 'wpforo' ) . '" onclick="return confirm(\'' . __( 'Are you sure, you want to deactivate this user?', 'wpforo' ) . '\')">' . __( 'Deactivate', 'wpforo' ) . '</a>',
104
+ 'delete' => '<a href="' . esc_url( $delete ) . '" title="' . __( 'Delete', 'wpforo' ) . '">' . __( 'Delete', 'wpforo' ) . '</a>',
105
+ ];
106
+
107
+ if( ! WPF()->usergroup->can( 'em' ) ) {
108
+ unset( $actions['edit_user'] );
109
+ unset( $actions['edit_profile'] );
110
+ }
111
+ if( ! WPF()->usergroup->can( 'bm' ) || intval( $item['userid'] ) === WPF()->current_userid ) {
112
+ unset( $actions['ban'] );
113
+ unset( $actions['unban'] );
114
+ }
115
+ if( $item['status'] === 'banned' ) {
116
+ unset( $actions['ban'] );
117
+ } else {
118
+ unset( $actions['unban'] );
119
+ }
120
+ if( $item['status'] === 'inactive' ) {
121
+ unset( $actions['deactivate'] );
122
+ } else {
123
+ unset( $actions['activate'] );
124
+ }
125
+ if( ! WPF()->usergroup->can( 'dm' ) || intval( $item['userid'] ) === WPF()->current_userid ) {
126
+ unset( $actions['delete'] );
127
+ }
128
+
129
+ //Return the title contents
130
+ return sprintf(
131
+ '%1$s %2$s',
132
+ /*$1%s*/ $item['userid'],
133
+ /*$2%s*/ $this->row_actions( $actions )
134
+ );
135
+ }
136
+
137
+
138
+ /** ************************************************************************
139
+ * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
140
+ * is given special treatment when columns are processed. It ALWAYS needs to
141
+ * have its own method.
142
+ *
143
+ * @param array $item A singular item (one full row's worth of data)
144
+ *
145
+ * @return string Text to be placed inside the column <td> (movie title only)
146
+ **************************************************************************@see WP_List_Table::::single_row_columns()
147
+ */
148
+ function column_cb( $item ) {
149
+ return sprintf(
150
+ '<input type="checkbox" name="%1$s[]" value="%2$s" %3$s>',
151
+ /*$1%s*/ 'userids', //Let's simply repurpose the table's singular label ("movie")
152
+ /*$2%s*/ $item['userid'], //The value of the checkbox should be the record's id
153
+ ( intval( $item['userid'] ) === WPF()->current_userid ) ? 'disabled' : ''
154
+ );
155
+ }
156
+
157
+
158
+ /** ************************************************************************
159
+ * REQUIRED! This method dictates the table's columns and titles. This should
160
+ * return an array where the key is the column slug (and class) and the value
161
+ * is the column's title text. If you need a checkbox for bulk actions, refer
162
+ * to the $columns array below.
163
+ *
164
+ * The 'cb' column is treated differently than the rest. If including a checkbox
165
+ * column in your table you must create a column_cb() method. If you don't need
166
+ * to bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
167
+ *
168
+ * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
169
+ **************************************************************************@see WP_List_Table::::single_row_columns()
170
+ */
171
+ function get_columns() {
172
+ $columns = [
173
+ 'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
174
+ 'userid' => __( 'ID', 'wpforo' ),
175
+ 'display_name' => __( 'Display Name', 'wpforo' ),
176
+ 'user_login' => __( 'Login', 'wpforo' ),
177
+ 'user_email' => __( 'Email', 'wpforo' ),
178
+ 'groupid' => __( 'Group', 'wpforo' ),
179
+ 'status' => __( 'Status', 'wpforo' ),
180
+ 'online_time' => __( 'Last Login', 'wpforo' ),
181
+ 'posts' => __( 'Forum Posts', 'wpforo' ),
182
+ 'blog_posts' => __( 'Blog Posts', 'wpforo' ),
183
+ 'blog_comments' => __( 'Blog Comments', 'wpforo' ),
184
+ ];
185
+
186
+ return $this->unset_not_permitted_cols( $columns );
187
+ }
188
+
189
+
190
+ /** ************************************************************************
191
+ * Optional. If you want one or more columns to be sortable (ASC/DESC toggle),
192
+ * you will need to register it here. This should return an array where the
193
+ * key is the column that needs to be sortable, and the value is db column to
194
+ * sort by. Often, the key and value will be the same, but this is not always
195
+ * the case (as the value is a column name from the database, not the list table).
196
+ *
197
+ * This method merely defines which columns should be sortable and makes them
198
+ * clickable - it does not handle the actual sorting. You still need to detect
199
+ * the ORDERBY and ORDER querystring variables within prepare_items() and sort
200
+ * your data accordingly (usually by modifying your query).
201
+ *
202
+ * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
203
+ **************************************************************************/
204
+ function get_sortable_columns() {
205
+ $columns = [
206
+ 'userid' => [ 'userid', false ], //true means it's already sorted
207
+ 'display_name' => [ 'display_name', false ],
208
+ 'user_login' => [ 'user_login', false ],
209
+ 'user_email' => [ 'user_email', false ],
210
+ 'groupid' => [ 'groupid', false ],
211
+ 'status' => [ 'status', false ],
212
+ 'online_time' => [ 'online_time', false ],
213
+ 'posts' => [ 'posts', false ],
214
+ ];
215
+
216
+ return $this->unset_not_permitted_cols( $columns );
217
+ }
218
+
219
+ private function unset_not_permitted_cols( $columns ) {
220
+ if( ! WPF()->usergroup->can( 'vmu' ) ) {
221
+ unset( $columns['user_login'] );
222
+ }
223
+ if( ! WPF()->usergroup->can( 'vmm' ) ) {
224
+ unset( $columns['user_email'] );
225
+ }
226
+ if( ! WPF()->usergroup->can( 'vmg' ) ) {
227
+ unset( $columns['groupid'] );
228
+ }
229
+ if( ! WPF()->usergroup->can( 'bm' ) ) {
230
+ unset( $columns['status'] );
231
+ }
232
+ if( ! WPF()->usergroup->can( 'vms' ) ) {
233
+ unset( $columns['signature'] );
234
+ }
235
+
236
+ return $columns;
237
+ }
238
+
239
+
240
+ /** ************************************************************************
241
+ * Optional. If you need to include bulk actions in your list table, this is
242
+ * the place to define them. Bulk actions are an associative array in the format
243
+ * 'slug'=>'Visible Title'
244
+ *
245
+ * If this method returns an empty value, no bulk action will be rendered. If
246
+ * you specify any bulk actions, the bulk actions box will be rendered with
247
+ * the table automatically on display().
248
+ *
249
+ * Also note that list tables are not automatically wrapped in <form> elements,
250
+ * so you will need to create those manually in order for bulk actions to function.
251
+ *
252
+ * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
253
+ **************************************************************************/
254
+ function get_bulk_actions() {
255
+ $actions = [
256
+ 'activate' => __( 'Activate', 'wpforo' ),
257
+ 'deactivate' => __( 'Deactivate', 'wpforo' ),
258
+ 'ban' => __( 'Ban', 'wpforo' ),
259
+ 'unban' => __( 'Unban', 'wpforo' ),
260
+ 'delete' => __( 'Delete', 'wpforo' ),
261
+ ];
262
+ if( ! WPF()->usergroup->can( 'bm' ) ) {
263
+ unset( $actions['ban'] );
264
+ unset( $actions['unban'] );
265
+ unset( $actions['activate'] );
266
+ unset( $actions['deactivate'] );
267
+ }
268
+ if( ! WPF()->usergroup->can( 'dm' ) ) {
269
+ unset( $actions['delete'] );
270
+ }
271
+
272
+ return $actions;
273
+ }
274
+
275
+ function extra_tablenav( $which ) {
276
+ $name = 'new_groupid' . ( $which === 'bottom' ? '2' : '' );
277
+ printf(
278
+ '<div class="alignleft actions">
279
+ <label class="screen-reader-text" for="new_role">%1$s</label>
280
+ <select name="%2$s" id="new_role">
281
+ <option value="-1">%1$s</option>
282
+ %3$s
283
+ </select>
284
+ <input type="submit" name="change_group" class="button" value="%4$s">
285
+ </div>',
286
+ __( 'Change usergroup to…', 'wpforo' ),
287
+ $name,
288
+ WPF()->usergroup->get_selectbox( [], 4 ),
289
+ __( 'Change', 'wpforo' )
290
+ );
291
+ }
292
+
293
+
294
+ /** ************************************************************************
295
+ * REQUIRED! This is where you prepare your data for display. This method will
296
+ * usually be used to query the database, sort and filter the data, and generally
297
+ * get it ready to be displayed. At a minimum, we should set $this->items and
298
+ * $this->set_pagination_args(), although the following properties and methods
299
+ * are frequently interacted with here...
300
+ *
301
+ * @uses $this->_column_headers
302
+ * @uses $this->items
303
+ * @uses $this->get_columns()
304
+ * @uses $this->get_sortable_columns()
305
+ * @uses $this->get_pagenum()
306
+ * @uses $this->set_pagination_args()
307
+ **************************************************************************/
308
+ function prepare_items() {
309
+ /**
310
+ * First, lets decide how many records per page to show
311
+ */
312
+ $per_page = wpforo_get_option( 'count_per_page', 10 );
313
+
314
+
315
+ /**
316
+ * REQUIRED. Now we need to define our column headers. This includes a complete
317
+ * array of columns to be displayed (slugs & titles), a list of columns
318
+ * to keep hidden, and a list of columns that are sortable. Each of these
319
+ * can be defined in another method (as we've done here) before being
320
+ * used to build the value for our _column_headers property.
321
+ */
322
+ $columns = $this->get_columns();
323
+ $hidden = [];
324
+ $sortable = $this->get_sortable_columns();
325
+
326
+
327
+ /**
328
+ * REQUIRED. Finally, we build an array to be used by the class for column
329
+ * headers. The $this->_column_headers property takes an array which contains
330
+ * 3 other arrays. One for all columns, one for hidden columns, and one
331
+ * for sortable columns.
332
+ */
333
+ $this->_column_headers = [ $columns, $hidden, $sortable ];
334
+
335
+ $search_fields = [
336
+ 'title' => 'title',
337
+ 'display_name' => 'display_name',
338
+ 'user_login' => 'user_login',
339
+ 'user_email' => 'user_email',
340
+ 'signature' => 'signature',
341
+ ];
342
+ $search_fields = $this->unset_not_permitted_cols( $search_fields );
343
+
344
+ /**
345
+ * REQUIRED. Now we can add our *sorted* data to the items' property, where
346
+ * it can be used by the rest of the class.
347
+ */
348
+ $args = [];
349
+ if( $s = wpfval( $_REQUEST, 's' ) ) $args['include'] = WPF()->member->search( $s, $search_fields );
350
+ $orderby = wpfval( $_REQUEST, 'orderby' );
351
+ $order = strtoupper( wpfval( $_REQUEST, 'order' ) );
352
+ $filter_by_group = $this->get_filter_by_group_var();
353
+ $filter_by_status = $this->get_filter_by_status_var();
354
+ if( $filter_by_group !== - 1 ) $args['groupid'] = $filter_by_group;
355
+ if( $filter_by_status !== - 1 ) $args['status'] = (array) sanitize_text_field( $filter_by_status );
356
+ if( array_key_exists( $orderby, $sortable ) ) $args['orderby'] = sanitize_text_field( $orderby );
357
+ if( in_array( $order, [ 'ASC', 'DESC' ] ) ) $args['order'] = sanitize_text_field( $order );
358
+
359
+ $paged = $this->get_pagenum();
360
+ $args['offset'] = ( $paged - 1 ) * $per_page;
361
+ $args['row_count'] = $per_page;
362
+
363
+ $items_count = 0;
364
+ $this->items = ( isset( $args['include'] ) && empty( $args['include'] ) ? [] : WPF()->member->get_members( $args, $items_count ) );
365
+
366
+ $this->wpfitems_count = $items_count;
367
+
368
+ $this->set_pagination_args( [
369
+ 'total_items' => $items_count, //WE have to calculate the total number of items
370
+ 'per_page' => $per_page, //WE have to determine how many items to show on a page
371
+ 'total_pages' => ceil( $items_count / $per_page ) //WE have to calculate the total number of pages
372
+ ] );
373
+ }
374
+
375
+ public function get_filter_by_group_var() {
376
+ $filter_by_group = wpfval( $_REQUEST, 'filter_by_group' );
377
+ if( ! is_null( $filter_by_group ) && $filter_by_group !== '-1' ) {
378
+ $groupid = $filter_by_group;
379
+ } else {
380
+ $groupid = - 1;
381
+ }
382
+
383
+ return intval( $groupid );
384
+ }
385
+
386
+ public function get_filter_by_status_var() {
387
+ $filter_by_status = wpfval( $_REQUEST, 'filter_by_status' );
388
+ if( ! is_null( $filter_by_status ) && $filter_by_status !== '-1' ) {
389
+ $status = $filter_by_status;
390
+ } else {
391
+ $status = - 1;
392
+ }
393
+
394
+ return $status;
395
+ }
396
+
397
+ public function groups_dropdown() { ?>
398
+ <label>
399
+ <select name="filter_by_group">
400
+ <option value="-1">-- <?php _e( 'filter by group', 'wpforo' ) ?> --</option>
401
+ <?php $selected = $this->get_filter_by_group_var();
402
+ foreach( WPF()->usergroup->get_usergroups() as $group ) {
403
+ $group['groupid'] = intval( $group['groupid'] );
404
+ if( $group['groupid'] !== 4 ) {
405
+ printf(
406
+ '<option value="%1$s" %2$s>%3$s</option>',
407
+ $group['groupid'],
408
+ ( $group['groupid'] === $selected ) ? 'selected' : '',
409
+ esc_html( $group['name'] )
410
+ );
411
+ }
412
+ }
413
+ ?>
414
+ </select>
415
+ </label>
416
+ <?php
417
+ }
418
+
419
+ public function status_dropdown() { ?>
420
+ <label>
421
+ <select name="filter_by_status">
422
+ <option value="-1">-- <?php _e( 'filter by status', 'wpforo' ) ?> --</option>
423
+ <?php
424
+ if( $statuses = WPF()->member->get_distinct_status() ) {
425
+ $selected = $this->get_filter_by_status_var();
426
+ foreach( $statuses as $status ) {
427
+ printf(
428
+ '<option value="%1$s" %2$s>%3$s</option>',
429
+ esc_attr( $status ),
430
+ ( $status === $selected ) ? 'selected' : '',
431
+ esc_html( $status )
432
+ );
433
+ }
434
+ }
435
+ ?>
436
+ </select>
437
+ </label>
438
+ <?php
439
+ }
440
+ }
wpf-admin/includes/moderation-listtable.php → admin/listtables/Moderations.php RENAMED
@@ -1,26 +1,32 @@
1
  <?php
2
- if(!class_exists('WP_List_Table')){
3
- require_once( ABSPATH . 'wp-admin/includes/class-wp-screen.php' );
4
- require_once( ABSPATH . 'wp-admin/includes/screen.php' );
5
- require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
6
- require_once( ABSPATH . 'wp-admin/includes/template.php' );
7
- }
8
 
9
- class wpForoModeratonsListTable extends WP_List_Table {
 
 
 
 
 
 
 
10
 
11
- public $wpfitems_count;
 
 
12
 
13
  /** ************************************************************************
14
  * REQUIRED. Set up a constructor that references the parent constructor. We
15
  * use the parent reference to set some default configs.
16
  ***************************************************************************/
17
- function __construct(){
18
  //Set parent defaults
19
- parent::__construct( array(
20
- 'singular' => 'moderation', //singular name of the listed records
21
- 'plural' => 'moderations', //plural name of the listed records
22
- 'ajax' => false //does this table support ajax?
23
- ) );
 
 
 
24
 
25
  }
26
 
@@ -44,17 +50,19 @@ class wpForoModeratonsListTable extends WP_List_Table {
44
  *
45
  * @param array $item A singular item (one full row's worth of data)
46
  * @param string $column_name The name/slug of the column to be processed
 
47
  * @return string Text or HTML to be placed inside the column <td>
48
  **************************************************************************/
49
  function column_default( $item, $column_name ) {
50
- switch ( $column_name ) {
51
- case 'title':
52
- return apply_filters('wpforo_moderation_listtable_column_id', $item[ $column_name ], $item);
53
  case 'userid':
54
  $userdata = get_userdata( $item[ $column_name ] );
 
55
  return ( ! empty( $userdata->user_nicename ) ? urldecode( $userdata->user_nicename ) : $item[ $column_name ] );
56
  case 'is_first_post':
57
- return ( $item[ $column_name ] ) ? __( 'TOPIC', 'wpforo' ) : __( 'POST', 'wpforo' );
58
  case 'private':
59
  return ( $item[ $column_name ] ) ? __( 'YES', 'wpforo' ) : __( 'NO', 'wpforo' );
60
  default:
@@ -75,30 +83,32 @@ class wpForoModeratonsListTable extends WP_List_Table {
75
  * will need to generate the URLs yourself. You could even ensure the links
76
  *
77
  *
78
- * @see WP_List_Table::::single_row_columns()
79
  * @param array $item A singular item (one full row's worth of data)
 
80
  * @return string Text to be placed inside the column <td> (movie title only)
81
- **************************************************************************/
82
- function column_postid($item){
83
- $vhref = WPF()->moderation->get_view_url($item['postid']);
 
84
  //Build row actions
85
- $actions = array( 'view' => '<a href="' . $vhref . '" target="_blank">'.__('View', 'wpforo').'</a>' );
86
- if( $this->get_filter_by_status_var() ){
87
- $ahref = wp_nonce_url(admin_url( sprintf('admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s','wpforo-moderations', 'wpforo_dashboard_post_approve', $item['postid']) ), 'wpforo-approve-post-' . $item['postid']);
88
- $actions['wpfapprove'] = '<a href="' . $ahref . '">'. __('Approve', 'wpforo') .'</a>';
89
- }else{
90
- $uhref = wp_nonce_url(admin_url( sprintf('admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s','wpforo-moderations', 'wpforo_dashboard_post_unapprove', $item['postid']) ), 'wpforo-unapprove-post-' . $item['postid']);
91
- $actions['wpfunapprove'] = '<a href="' . $uhref . '">'. __('Unapprove', 'wpforo') .'</a>';
92
  }
93
- $dhref = wp_nonce_url(admin_url( sprintf('admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s','wpforo-moderations', 'wpforo_dashboard_post_delete', $item['postid']) ), 'wpforo-delete-post-' . $item['postid']);
94
- $actions['delete'] = '<a onclick="return confirm(\'' . __( "Are you sure you want to DELETE this item?", 'wpforo' ) . '\');" href="' . $dhref . '">' . __('Delete', 'wpforo') . '</a>';
95
 
96
- $actions = apply_filters('wpforo_moderation_listtable_actions', $actions, $item);
97
 
98
  //Return the title contents
99
- return sprintf('%1$s %2$s',
 
100
  /*$1%s*/ $item['postid'],
101
- /*$2%s*/ $this->row_actions($actions)
102
  );
103
  }
104
 
@@ -106,15 +116,16 @@ class wpForoModeratonsListTable extends WP_List_Table {
106
  /** ************************************************************************
107
  * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
108
  * is given special treatment when columns are processed. It ALWAYS needs to
109
- * have it's own method.
110
  *
111
- * @see WP_List_Table::::single_row_columns()
112
  * @param array $item A singular item (one full row's worth of data)
 
113
  * @return string Text to be placed inside the column <td> (movie title only)
114
- **************************************************************************/
115
- function column_cb($item){
 
116
  return sprintf(
117
- '<input type="checkbox" name="%1$s[]" value="%2$s" />',
118
  /*$1%s*/ 'postids', //Let's simply repurpose the table's singular label ("movie")
119
  /*$2%s*/ $item['postid'] //The value of the checkbox should be the record's id
120
  );
@@ -129,21 +140,21 @@ class wpForoModeratonsListTable extends WP_List_Table {
129
  *
130
  * The 'cb' column is treated differently than the rest. If including a checkbox
131
  * column in your table you must create a column_cb() method. If you don't need
132
- * bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
133
  *
134
- * @see WP_List_Table::::single_row_columns()
135
  * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
136
- **************************************************************************/
137
- function get_columns(){
138
- return array(
 
139
  'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
140
- 'postid' => __('ID', 'wpforo'),
141
- 'title' => __('Title', 'wpforo'),
142
- 'is_first_post' => __('Type', 'wpforo'),
143
- 'userid' => __('Created By', 'wpforo'),
144
- 'created' => __('Created', 'wpforo'),
145
- 'private' => __('Private', 'wpforo')
146
- );
147
  }
148
 
149
 
@@ -162,14 +173,14 @@ class wpForoModeratonsListTable extends WP_List_Table {
162
  * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
163
  **************************************************************************/
164
  function get_sortable_columns() {
165
- return array(
166
- 'postid' => array( 'postid', false ), //true means it's already sorted
167
- 'title' => array( 'title', false ),
168
- 'is_first_post' => array( 'is_first_post', false ),
169
- 'userid' => array( 'userid', false ),
170
- 'created' => array( 'created', false ),
171
- 'private' => array( 'private', false )
172
- );
173
  }
174
 
175
 
@@ -188,13 +199,14 @@ class wpForoModeratonsListTable extends WP_List_Table {
188
  * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
189
  **************************************************************************/
190
  function get_bulk_actions() {
191
- $bulk_actions = array();
192
- if( $this->get_filter_by_status_var() ){
193
- $bulk_actions['approve'] = __('Approve', 'wpforo');
194
- }else{
195
- $bulk_actions['unapprove'] = __('Unapprove', 'wpforo');
196
  }
197
- $bulk_actions['delete'] = __('Delete', 'wpforo');
 
198
  return $bulk_actions;
199
  }
200
 
@@ -206,7 +218,6 @@ class wpForoModeratonsListTable extends WP_List_Table {
206
  * $this->set_pagination_args(), although the following properties and methods
207
  * are frequently interacted with here...
208
  *
209
- * @global WPDB $wpdb
210
  * @uses $this->_column_headers
211
  * @uses $this->items
212
  * @uses $this->get_columns()
@@ -218,7 +229,7 @@ class wpForoModeratonsListTable extends WP_List_Table {
218
  /**
219
  * First, lets decide how many records per page to show
220
  */
221
- $per_page = get_option('wpforo_count_per_page', 10);
222
 
223
 
224
  /**
@@ -228,8 +239,8 @@ class wpForoModeratonsListTable extends WP_List_Table {
228
  * can be defined in another method (as we've done here) before being
229
  * used to build the value for our _column_headers property.
230
  */
231
- $columns = $this->get_columns();
232
- $hidden = array();
233
  $sortable = $this->get_sortable_columns();
234
 
235
 
@@ -239,90 +250,119 @@ class wpForoModeratonsListTable extends WP_List_Table {
239
  * 3 other arrays. One for all columns, one for hidden columns, and one
240
  * for sortable columns.
241
  */
242
- $this->_column_headers = array($columns, $hidden, $sortable);
243
 
244
  /**
245
- * REQUIRED. Now we can add our *sorted* data to the items property, where
246
  * it can be used by the rest of the class.
247
  */
248
- $args = array('status' => $this->get_filter_by_status_var(), 'orderby' => '`created` DESC, `postid` DESC');
249
- if( $s = wpfval($_REQUEST, 's') ){
250
- $args['include'] = WPF()->moderation->search($s);
251
  }
252
  $filter_by_userid = $this->get_filter_by_userid_var();
253
- $orderby = wpfval($_REQUEST, 'orderby');
254
- $order = strtoupper( wpfval($_REQUEST, 'order') );
255
- if( $filter_by_userid !== -1 ) $args['userid'] = $filter_by_userid;
256
- if( array_key_exists($orderby, $sortable) ) $args['orderby'] = sanitize_text_field($orderby);
257
- if( in_array( $order, array('ASC', 'DESC') ) ) $args['order'] = sanitize_text_field($order);
258
-
259
- $paged = $this->get_pagenum();
260
- $args['offset'] = ($paged - 1) * $per_page;
 
 
 
 
 
261
  $args['row_count'] = $per_page;
262
 
263
  $items_count = 0;
264
- $this->items = ( isset($args['include']) && empty($args['include']) ? array() : WPF()->post->get_posts($args, $items_count) );
265
 
266
  $this->wpfitems_count = $items_count;
267
 
268
- $this->set_pagination_args( array(
269
- 'total_items' => $items_count, //WE have to calculate the total number of items
270
- 'per_page' => $per_page, //WE have to determine how many items to show on a page
271
- 'total_pages' => ceil($items_count/$per_page) //WE have to calculate the total number of pages
272
- ) );
273
  }
274
 
275
- public function get_filter_by_status_var(){
276
- $filter_by_status = wpfval($_REQUEST, 'filter_by_status');
277
- if( !is_null($filter_by_status) && $filter_by_status !== '-1' ){
278
- $status = intval($filter_by_status);
279
- }else{
280
  $status = 1;
281
  }
282
 
283
  return $status;
284
- }
285
-
286
- public function get_filter_by_userid_var(){
287
- $filter_by_userid = wpfval($_REQUEST, 'filter_by_userid');
288
- if( !is_null($filter_by_userid) && $filter_by_userid !== '-1' ){
289
- $userid = wpforo_bigintval($filter_by_userid);
290
- }else{
291
- $userid = -1;
292
- }
293
- return $userid;
294
- }
295
-
296
- public function users_dropdown(){
 
 
 
 
 
 
 
297
  ?>
298
- <select name="filter_by_userid">
299
- <option value="-1">-- <?php _e('All Users', 'wpforo'); ?> --</option>
300
-
301
- <?php
302
- if( $userids = WPF()->moderation->get_distinct_userids( $this->get_filter_by_status_var() ) ){
303
- $current_userid = $this->get_filter_by_userid_var();
304
- foreach ($userids as $userid){
305
- $userid = wpforo_bigintval($userid);
306
- $userdata = get_userdata($userid);
307
- ?>
308
- <option value="<?php echo $userid ?>" <?php echo ($current_userid === $userid ? 'selected' : '') ?> > <?php echo (!empty($userdata->user_nicename) ? urldecode($userdata->user_nicename) : $userid) ?> </option>
309
- <?php
 
 
310
  }
311
- }
312
- ?>
313
- </select>
314
  <?php
315
  }
316
 
317
- public function status_dropdown(){
318
  $filter_by_status = $this->get_filter_by_status_var();
319
- if( $statuses = WPF()->moderation->post_statuses ) : ?>
320
- <select name="filter_by_status">
321
- <?php foreach ($statuses as $key => $status) : ?>
322
- <option value="<?php echo esc_attr($key) ?>" <?php echo ( $filter_by_status === $key ? 'selected' : '' ) ?>><?php echo esc_html( $status ) ?></option>
323
- <?php endforeach; ?>
324
- </select>
 
 
325
  <?php
326
  endif;
327
  }
328
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
 
 
 
 
 
 
2
 
3
+ namespace wpforo\admin\listtables;
4
+
5
+ use WP_List_Table;
6
+
7
+ require_once( ABSPATH . 'wp-admin/includes/template.php' );
8
+ require_once( ABSPATH . 'wp-admin/includes/screen.php' );
9
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-screen.php' );
10
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
11
 
12
+ class Moderations extends WP_List_Table {
13
+
14
+ public $wpfitems_count;
15
 
16
  /** ************************************************************************
17
  * REQUIRED. Set up a constructor that references the parent constructor. We
18
  * use the parent reference to set some default configs.
19
  ***************************************************************************/
20
+ function __construct() {
21
  //Set parent defaults
22
+ parent::__construct(
23
+ [
24
+ 'singular' => 'moderation', //singular name of the listed records
25
+ 'plural' => 'moderations', //plural name of the listed records
26
+ 'ajax' => false, //does this table support ajax?
27
+ 'screen' => 'wpForoModerations',
28
+ ]
29
+ );
30
 
31
  }
32
 
50
  *
51
  * @param array $item A singular item (one full row's worth of data)
52
  * @param string $column_name The name/slug of the column to be processed
53
+ *
54
  * @return string Text or HTML to be placed inside the column <td>
55
  **************************************************************************/
56
  function column_default( $item, $column_name ) {
57
+ switch( $column_name ) {
58
+ case 'title':
59
+ return apply_filters( 'wpforo_admin_listtables_moderations_column_title', $item[ $column_name ], $item );
60
  case 'userid':
61
  $userdata = get_userdata( $item[ $column_name ] );
62
+
63
  return ( ! empty( $userdata->user_nicename ) ? urldecode( $userdata->user_nicename ) : $item[ $column_name ] );
64
  case 'is_first_post':
65
+ return ( $item[ $column_name ] ) ? __( 'TOPIC', 'wpforo' ) : __( 'REPLY', 'wpforo' );
66
  case 'private':
67
  return ( $item[ $column_name ] ) ? __( 'YES', 'wpforo' ) : __( 'NO', 'wpforo' );
68
  default:
83
  * will need to generate the URLs yourself. You could even ensure the links
84
  *
85
  *
 
86
  * @param array $item A singular item (one full row's worth of data)
87
+ *
88
  * @return string Text to be placed inside the column <td> (movie title only)
89
+ **************************************************************************@see WP_List_Table::::single_row_columns()
90
+ */
91
+ function column_postid( $item ) {
92
+ $vhref = WPF()->moderation->get_view_url( $item['postid'] );
93
  //Build row actions
94
+ $actions = [ 'view' => '<a href="' . $vhref . '" target="_blank">' . __( 'View', 'wpforo' ) . '</a>' ];
95
+ if( $this->get_filter_by_status_var() ) {
96
+ $ahref = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s', wpforo_prefix_slug( 'moderations' ), 'dashboard_post_approve', $item['postid'] ) ), 'wpforo-approve-post-' . $item['postid'] );
97
+ $actions['wpfapprove'] = '<a href="' . $ahref . '">' . __( 'Approve', 'wpforo' ) . '</a>';
98
+ } else {
99
+ $uhref = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s', wpforo_prefix_slug( 'moderations' ), 'dashboard_post_unapprove', $item['postid'] ) ), 'wpforo-unapprove-post-' . $item['postid'] );
100
+ $actions['wpfunapprove'] = '<a href="' . $uhref . '">' . __( 'Unapprove', 'wpforo' ) . '</a>';
101
  }
102
+ $dhref = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&postid=%3$s', wpforo_prefix_slug( 'moderations' ), 'dashboard_post_delete', $item['postid'] ) ), 'wpforo-delete-post-' . $item['postid'] );
103
+ $actions['delete'] = '<a onclick="return confirm(\'' . __( "Are you sure you want to DELETE this item?", 'wpforo' ) . '\');" href="' . $dhref . '">' . __( 'Delete', 'wpforo' ) . '</a>';
104
 
105
+ $actions = apply_filters( 'wpforo_admin_listtables_moderations_actions', $actions, $item );
106
 
107
  //Return the title contents
108
+ return sprintf(
109
+ '%1$s %2$s',
110
  /*$1%s*/ $item['postid'],
111
+ /*$2%s*/ $this->row_actions( $actions )
112
  );
113
  }
114
 
116
  /** ************************************************************************
117
  * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
118
  * is given special treatment when columns are processed. It ALWAYS needs to
119
+ * have its own method.
120
  *
 
121
  * @param array $item A singular item (one full row's worth of data)
122
+ *
123
  * @return string Text to be placed inside the column <td> (movie title only)
124
+ **************************************************************************@see WP_List_Table::::single_row_columns()
125
+ */
126
+ function column_cb( $item ) {
127
  return sprintf(
128
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
129
  /*$1%s*/ 'postids', //Let's simply repurpose the table's singular label ("movie")
130
  /*$2%s*/ $item['postid'] //The value of the checkbox should be the record's id
131
  );
140
  *
141
  * The 'cb' column is treated differently than the rest. If including a checkbox
142
  * column in your table you must create a column_cb() method. If you don't need
143
+ * to bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
144
  *
 
145
  * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
146
+ **************************************************************************@see WP_List_Table::::single_row_columns()
147
+ */
148
+ function get_columns() {
149
+ return [
150
  'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
151
+ 'postid' => __( 'ID', 'wpforo' ),
152
+ 'title' => __( 'Title', 'wpforo' ),
153
+ 'is_first_post' => __( 'Type', 'wpforo' ),
154
+ 'userid' => __( 'Created By', 'wpforo' ),
155
+ 'created' => __( 'Created', 'wpforo' ),
156
+ 'private' => __( 'Private', 'wpforo' ),
157
+ ];
158
  }
159
 
160
 
173
  * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
174
  **************************************************************************/
175
  function get_sortable_columns() {
176
+ return [
177
+ 'postid' => [ 'postid', false ], //true means it's already sorted
178
+ 'title' => [ 'title', false ],
179
+ 'is_first_post' => [ 'is_first_post', false ],
180
+ 'userid' => [ 'userid', false ],
181
+ 'created' => [ 'created', false ],
182
+ 'private' => [ 'private', false ],
183
+ ];
184
  }
185
 
186
 
199
  * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
200
  **************************************************************************/
201
  function get_bulk_actions() {
202
+ $bulk_actions = [];
203
+ if( $this->get_filter_by_status_var() ) {
204
+ $bulk_actions['approve'] = __( 'Approve', 'wpforo' );
205
+ } else {
206
+ $bulk_actions['unapprove'] = __( 'Unapprove', 'wpforo' );
207
  }
208
+ $bulk_actions['delete'] = __( 'Delete', 'wpforo' );
209
+
210
  return $bulk_actions;
211
  }
212
 
218
  * $this->set_pagination_args(), although the following properties and methods
219
  * are frequently interacted with here...
220
  *
 
221
  * @uses $this->_column_headers
222
  * @uses $this->items
223
  * @uses $this->get_columns()
229
  /**
230
  * First, lets decide how many records per page to show
231
  */
232
+ $per_page = wpforo_get_option( 'count_per_page', 10 );
233
 
234
 
235
  /**
239
  * can be defined in another method (as we've done here) before being
240
  * used to build the value for our _column_headers property.
241
  */
242
+ $columns = $this->get_columns();
243
+ $hidden = [];
244
  $sortable = $this->get_sortable_columns();
245
 
246
 
250
  * 3 other arrays. One for all columns, one for hidden columns, and one
251
  * for sortable columns.
252
  */
253
+ $this->_column_headers = [ $columns, $hidden, $sortable ];
254
 
255
  /**
256
+ * REQUIRED. Now we can add our *sorted* data to the items' property, where
257
  * it can be used by the rest of the class.
258
  */
259
+ $args = [ 'status' => $this->get_filter_by_status_var(), 'orderby' => '`created` DESC, `postid` DESC' ];
260
+ if( $s = wpfval( $_REQUEST, 's' ) ) {
261
+ $args['include'] = WPF()->moderation->search( $s );
262
  }
263
  $filter_by_userid = $this->get_filter_by_userid_var();
264
+ $orderby = wpfval( $_REQUEST, 'orderby' );
265
+ $order = strtoupper( wpfval( $_REQUEST, 'order' ) );
266
+ if( $filter_by_userid !== - 1 ) $args['userid'] = $filter_by_userid;
267
+
268
+ if( $type = $this->get_filter_by_type_var() ){
269
+ $args['is_first_post'] = $type === 'topic';
270
+ }
271
+
272
+ if( array_key_exists( $orderby, $sortable ) ) $args['orderby'] = sanitize_text_field( $orderby );
273
+ if( in_array( $order, [ 'ASC', 'DESC' ] ) ) $args['order'] = sanitize_text_field( $order );
274
+
275
+ $paged = $this->get_pagenum();
276
+ $args['offset'] = ( $paged - 1 ) * $per_page;
277
  $args['row_count'] = $per_page;
278
 
279
  $items_count = 0;
280
+ $this->items = ( isset( $args['include'] ) && empty( $args['include'] ) ? [] : WPF()->post->get_posts( $args, $items_count ) );
281
 
282
  $this->wpfitems_count = $items_count;
283
 
284
+ $this->set_pagination_args( [
285
+ 'total_items' => $items_count, //WE have to calculate the total number of items
286
+ 'per_page' => $per_page, //WE have to determine how many items to show on a page
287
+ 'total_pages' => ceil( $items_count / $per_page ) //WE have to calculate the total number of pages
288
+ ] );
289
  }
290
 
291
+ public function get_filter_by_status_var() {
292
+ $filter_by_status = wpfval( $_REQUEST, 'filter_by_status' );
293
+ if( ! is_null( $filter_by_status ) && $filter_by_status !== '-1' ) {
294
+ $status = intval( $filter_by_status );
295
+ } else {
296
  $status = 1;
297
  }
298
 
299
  return $status;
300
+ }
301
+
302
+ public function get_filter_by_type_var() {
303
+ $filter_by_type = wpfval( $_REQUEST, 'filter_by_type' );
304
+
305
+ return in_array( $filter_by_type, [ 'topic', 'reply' ], true ) ? $filter_by_type : '';
306
+ }
307
+
308
+ public function get_filter_by_userid_var() {
309
+ $filter_by_userid = wpfval( $_REQUEST, 'filter_by_userid' );
310
+ if( ! is_null( $filter_by_userid ) && $filter_by_userid !== '-1' ) {
311
+ $userid = wpforo_bigintval( $filter_by_userid );
312
+ } else {
313
+ $userid = - 1;
314
+ }
315
+
316
+ return $userid;
317
+ }
318
+
319
+ public function users_dropdown() {
320
  ?>
321
+ <label>
322
+ <select name="filter_by_userid">
323
+ <option value="-1">-- <?php _e( 'All Users', 'wpforo' ); ?> --</option>
324
+
325
+ <?php
326
+ if( $userids = WPF()->moderation->get_distinct_userids( $this->get_filter_by_status_var() ) ) {
327
+ $current_userid = $this->get_filter_by_userid_var();
328
+ foreach( $userids as $userid ) {
329
+ $userid = wpforo_bigintval( $userid );
330
+ $userdata = get_userdata( $userid );
331
+ ?>
332
+ <option value="<?php echo $userid ?>" <?php echo( $current_userid === $userid ? 'selected' : '' ) ?> > <?php echo( ! empty( $userdata->user_nicename ) ? urldecode( $userdata->user_nicename ) : $userid ) ?> </option>
333
+ <?php
334
+ }
335
  }
336
+ ?>
337
+ </select>
338
+ </label>
339
  <?php
340
  }
341
 
342
+ public function status_dropdown() {
343
  $filter_by_status = $this->get_filter_by_status_var();
344
+ if( $statuses = WPF()->moderation->post_statuses ) : ?>
345
+ <label>
346
+ <select name="filter_by_status">
347
+ <?php foreach( $statuses as $key => $status ) : ?>
348
+ <option value="<?php echo esc_attr( $key ) ?>" <?php echo( $filter_by_status === $key ? 'selected' : '' ) ?>><?php echo esc_html( $status ) ?></option>
349
+ <?php endforeach; ?>
350
+ </select>
351
+ </label>
352
  <?php
353
  endif;
354
  }
355
+
356
+ public function type_dropdown() {
357
+ $filter_by_type = $this->get_filter_by_type_var();
358
+ ?>
359
+ <label>
360
+ <select name="filter_by_type">
361
+ <option value="">-- <?php _e('All', 'wpforo') ?> --</option>
362
+ <option value="topic" <?php echo( $filter_by_type === 'topic' ? 'selected' : '' ) ?>><?php _e('Topic', 'wpforo') ?></option>
363
+ <option value="reply" <?php echo( $filter_by_type === 'reply' ? 'selected' : '' ) ?>><?php _e('Reply', 'wpforo') ?></option>
364
+ </select>
365
+ </label>
366
+ <?php
367
+ }
368
+ }
wpf-admin/includes/phrase-listtable.php → admin/listtables/Phrases.php RENAMED
@@ -1,26 +1,30 @@
1
  <?php
2
- if(!class_exists('WP_List_Table')){
3
- require_once( ABSPATH . 'wp-admin/includes/class-wp-screen.php' );
4
- require_once( ABSPATH . 'wp-admin/includes/screen.php' );
5
- require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
6
- require_once( ABSPATH . 'wp-admin/includes/template.php' );
7
- }
8
 
9
- class wpForoPhrasesListTable extends WP_List_Table {
 
 
 
 
 
 
 
10
 
11
- public $wpfitems_count;
 
 
12
 
13
  /** ************************************************************************
14
  * REQUIRED. Set up a constructor that references the parent constructor. We
15
  * use the parent reference to set some default configs.
16
  ***************************************************************************/
17
- function __construct(){
18
  //Set parent defaults
19
- parent::__construct( array(
20
- 'singular' => 'phrase', //singular name of the listed records
21
- 'plural' => 'phrases', //plural name of the listed records
22
- 'ajax' => false //does this table support ajax?
23
- ) );
 
24
 
25
  }
26
 
@@ -44,13 +48,15 @@ class wpForoPhrasesListTable extends WP_List_Table {
44
  *
45
  * @param array $item A singular item (one full row's worth of data)
46
  * @param string $column_name The name/slug of the column to be processed
 
47
  * @return string Text or HTML to be placed inside the column <td>
48
  **************************************************************************/
49
  function column_default( $item, $column_name ) {
50
- switch ( $column_name ) {
51
  case 'langid':
52
- $language = WPF()->phrase->get_language($item[ $column_name ]);
53
- return ( ! empty( $language['name'] ) ? esc_html($language['name']) : __('default', 'wpforo') );
 
54
  default:
55
  return $item[ $column_name ];
56
  }
@@ -69,20 +75,22 @@ class wpForoPhrasesListTable extends WP_List_Table {
69
  * will need to generate the URLs yourself. You could even ensure the links
70
  *
71
  *
72
- * @see WP_List_Table::::single_row_columns()
73
  * @param array $item A singular item (one full row's worth of data)
 
74
  * @return string Text to be placed inside the column <td> (movie title only)
75
- **************************************************************************/
76
- function column_phraseid($item){
77
- $ehref = wp_nonce_url(admin_url( sprintf('admin.php?page=%1$s&wpfaction=%2$s&phraseid=%3$s','wpforo-phrases', 'wpforo_phrase_edit_form', $item['phraseid']) ), 'wpforo-phrase-edit-' . $item['phraseid']);
78
- $actions = array(
79
- 'edit' => '<a href="' . $ehref . '">' . __('Edit', 'wpforo') . '</a>'
80
- );
 
81
 
82
  //Return the title contents
83
- return sprintf('%1$s %2$s',
 
84
  /*$1%s*/ $item['phraseid'],
85
- /*$2%s*/ $this->row_actions($actions)
86
  );
87
  }
88
 
@@ -90,15 +98,16 @@ class wpForoPhrasesListTable extends WP_List_Table {
90
  /** ************************************************************************
91
  * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
92
  * is given special treatment when columns are processed. It ALWAYS needs to
93
- * have it's own method.
94
  *
95
- * @see WP_List_Table::::single_row_columns()
96
  * @param array $item A singular item (one full row's worth of data)
 
97
  * @return string Text to be placed inside the column <td> (movie title only)
98
- **************************************************************************/
99
- function column_cb($item){
 
100
  return sprintf(
101
- '<input type="checkbox" name="%1$s[]" value="%2$s" />',
102
  /*$1%s*/ 'phraseids', //Let's simply repurpose the table's singular label ("movie")
103
  /*$2%s*/ $item['phraseid'] //The value of the checkbox should be the record's id
104
  );
@@ -113,20 +122,20 @@ class wpForoPhrasesListTable extends WP_List_Table {
113
  *
114
  * The 'cb' column is treated differently than the rest. If including a checkbox
115
  * column in your table you must create a column_cb() method. If you don't need
116
- * bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
117
  *
118
- * @see WP_List_Table::::single_row_columns()
119
  * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
120
- **************************************************************************/
121
- function get_columns(){
122
- return array(
 
123
  'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
124
- 'phraseid' => __( 'ID', 'wpforo' ),
125
- 'phrase_key' => __( 'Original', 'wpforo' ),
126
  'phrase_value' => __( 'Translation', 'wpforo' ),
127
- 'package' => __( 'Package', 'wpforo' ),
128
- 'langid' => __( 'Language', 'wpforo' )
129
- );
130
  }
131
 
132
 
@@ -145,13 +154,13 @@ class wpForoPhrasesListTable extends WP_List_Table {
145
  * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
146
  **************************************************************************/
147
  function get_sortable_columns() {
148
- return array(
149
- 'phraseid' => array( 'phraseid', false ), //true means it's already sorted
150
- 'phrase_key' => array( 'phrase_key', false ),
151
- 'phrase_value' => array( 'phrase_value', false ),
152
- 'package' => array( 'package', false ),
153
- 'langid' => array( 'langid', false )
154
- );
155
  }
156
 
157
 
@@ -170,7 +179,7 @@ class wpForoPhrasesListTable extends WP_List_Table {
170
  * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
171
  **************************************************************************/
172
  function get_bulk_actions() {
173
- return array( 'edit' => __( 'Edit', 'wpforo' ) );
174
  }
175
 
176
 
@@ -181,7 +190,6 @@ class wpForoPhrasesListTable extends WP_List_Table {
181
  * $this->set_pagination_args(), although the following properties and methods
182
  * are frequently interacted with here...
183
  *
184
- * @global WPDB $wpdb
185
  * @uses $this->_column_headers
186
  * @uses $this->items
187
  * @uses $this->get_columns()
@@ -193,7 +201,7 @@ class wpForoPhrasesListTable extends WP_List_Table {
193
  /**
194
  * First, lets decide how many records per page to show
195
  */
196
- $per_page = get_option('wpforo_count_per_page', 10);
197
 
198
 
199
  /**
@@ -203,8 +211,8 @@ class wpForoPhrasesListTable extends WP_List_Table {
203
  * can be defined in another method (as we've done here) before being
204
  * used to build the value for our _column_headers property.
205
  */
206
- $columns = $this->get_columns();
207
- $hidden = array();
208
  $sortable = $this->get_sortable_columns();
209
 
210
 
@@ -214,81 +222,87 @@ class wpForoPhrasesListTable extends WP_List_Table {
214
  * 3 other arrays. One for all columns, one for hidden columns, and one
215
  * for sortable columns.
216
  */
217
- $this->_column_headers = array($columns, $hidden, $sortable);
218
 
219
  /**
220
- * REQUIRED. Now we can add our *sorted* data to the items property, where
221
  * it can be used by the rest of the class.
222
  */
223
- $args = array('langid' => $this->get_filter_by_langid_var());
224
- if( $s = wpfval($_REQUEST, 's') ) $args['include'] = WPF()->phrase->search($s);
225
- $orderby = wpfval($_REQUEST, 'orderby');
226
- $order = strtoupper( wpfval($_REQUEST, 'order') );
227
  $filter_by_package = $this->get_filter_by_package_var();
228
- if( $filter_by_package !== -1 ) $args['package'] = (array) $filter_by_package;
229
- if( in_array( $order, array('ASC', 'DESC') ) ) $args['order'] = sanitize_text_field($order);
230
- if( array_key_exists($orderby, $sortable) ) $args['orderby'] = sanitize_text_field($orderby);
231
 
232
- $paged = $this->get_pagenum();
233
- $args['offset'] = ($paged - 1) * $per_page;
234
  $args['row_count'] = $per_page;
235
 
236
  $items_count = 0;
237
- $this->items = ( isset($args['include']) && empty($args['include']) ? array() : WPF()->phrase->get_phrases($args, $items_count, true) );
238
 
239
  $this->wpfitems_count = $items_count;
240
 
241
- $this->set_pagination_args( array(
242
- 'total_items' => $items_count, //WE have to calculate the total number of items
243
- 'per_page' => $per_page, //WE have to determine how many items to show on a page
244
- 'total_pages' => ceil($items_count/$per_page) //WE have to calculate the total number of pages
245
- ) );
246
  }
247
 
248
- public function get_filter_by_langid_var(){
249
- $filter_by_langid = wpfval($_REQUEST, 'filter_by_langid');
250
- if( !is_null($filter_by_langid) && $filter_by_langid !== '-1' ){
251
  $langid = $filter_by_langid;
252
- }else{
253
- $langid = WPF()->general_options['lang'];
254
  }
255
- return intval($langid);
256
- }
257
-
258
- public function get_filter_by_package_var(){
259
- $filter_by_package = wpfval($_REQUEST, 'filter_by_package');
260
- if( !is_null($filter_by_package) && $filter_by_package !== '-1' ){
261
- $package = $filter_by_package;
262
- }else{
263
- $package = -1;
264
- }
265
- return $package;
266
- }
267
-
268
- public function packages_dropdown(){ ?>
269
- <select name="filter_by_package">
270
- <option value="-1">-- <?php _e('filter by package', 'wpforo') ?> --</option>
271
- <?php
272
- if( $packages = WPF()->phrase->get_distinct_packages() ){
273
- $selected = $this->get_filter_by_package_var();
274
- foreach( $packages as $package ){
275
- printf(
276
- '<option value="%1$s" %2$s>%3$s</option>',
277
- esc_attr($package),
278
- ($package === $selected) ? 'selected' : '',
279
- esc_html($package)
280
- );
 
 
 
 
281
  }
282
- }
283
- ?>
284
- </select>
285
  <?php
286
  }
287
 
288
- public function languages_dropdown(){ ?>
289
- <select name="filter_by_langid">
290
- <?php WPF()->phrase->show_lang_list( $this->get_filter_by_langid_var() ); ?>
291
- </select>
 
 
292
  <?php
293
  }
294
- }
1
  <?php
 
 
 
 
 
 
2
 
3
+ namespace wpforo\admin\listtables;
4
+
5
+ use WP_List_Table;
6
+
7
+ require_once( ABSPATH . 'wp-admin/includes/template.php' );
8
+ require_once( ABSPATH . 'wp-admin/includes/screen.php' );
9
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-screen.php' );
10
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
11
 
12
+ class Phrases extends WP_List_Table {
13
+
14
+ public $wpfitems_count;
15
 
16
  /** ************************************************************************
17
  * REQUIRED. Set up a constructor that references the parent constructor. We
18
  * use the parent reference to set some default configs.
19
  ***************************************************************************/
20
+ function __construct() {
21
  //Set parent defaults
22
+ parent::__construct( [
23
+ 'singular' => 'phrase', //singular name of the listed records
24
+ 'plural' => 'phrases', //plural name of the listed records
25
+ 'ajax' => false, //does this table support ajax?
26
+ 'screen' => 'wpForoPhrases',
27
+ ] );
28
 
29
  }
30
 
48
  *
49
  * @param array $item A singular item (one full row's worth of data)
50
  * @param string $column_name The name/slug of the column to be processed
51
+ *
52
  * @return string Text or HTML to be placed inside the column <td>
53
  **************************************************************************/
54
  function column_default( $item, $column_name ) {
55
+ switch( $column_name ) {
56
  case 'langid':
57
+ $language = WPF()->phrase->get_language( $item[ $column_name ] );
58
+
59
+ return ( ! empty( $language['name'] ) ? esc_html( $language['name'] ) : __( 'default', 'wpforo' ) );
60
  default:
61
  return $item[ $column_name ];
62
  }
75
  * will need to generate the URLs yourself. You could even ensure the links
76
  *
77
  *
 
78
  * @param array $item A singular item (one full row's worth of data)
79
+ *
80
  * @return string Text to be placed inside the column <td> (movie title only)
81
+ **************************************************************************@see WP_List_Table::::single_row_columns()
82
+ */
83
+ function column_phraseid( $item ) {
84
+ $ehref = wp_nonce_url( admin_url( sprintf( 'admin.php?page=%1$s&wpfaction=%2$s&phraseid=%3$s', wpforo_prefix_slug( 'phrases' ), 'phrase_edit_form', $item['phraseid'] ) ), 'wpforo-phrase-edit-' . $item['phraseid'] );
85
+ $actions = [
86
+ 'edit' => '<a href="' . $ehref . '">' . __( 'Edit', 'wpforo' ) . '</a>',
87
+ ];
88
 
89
  //Return the title contents
90
+ return sprintf(
91
+ '%1$s %2$s',
92
  /*$1%s*/ $item['phraseid'],
93
+ /*$2%s*/ $this->row_actions( $actions )
94
  );
95
  }
96
 
98
  /** ************************************************************************
99
  * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
100
  * is given special treatment when columns are processed. It ALWAYS needs to
101
+ * have its own method.
102
  *
 
103
  * @param array $item A singular item (one full row's worth of data)
104
+ *
105
  * @return string Text to be placed inside the column <td> (movie title only)
106
+ **************************************************************************@see WP_List_Table::::single_row_columns()
107
+ */
108
+ function column_cb( $item ) {
109
  return sprintf(
110
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
111
  /*$1%s*/ 'phraseids', //Let's simply repurpose the table's singular label ("movie")
112
  /*$2%s*/ $item['phraseid'] //The value of the checkbox should be the record's id
113
  );
122
  *
123
  * The 'cb' column is treated differently than the rest. If including a checkbox
124
  * column in your table you must create a column_cb() method. If you don't need
125
+ * to bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
126
  *
 
127
  * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
128
+ **************************************************************************@see WP_List_Table::::single_row_columns()
129
+ */
130
+ function get_columns() {
131
+ return [
132
  'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
133
+ 'phraseid' => __( 'ID', 'wpforo' ),
134
+ 'phrase_key' => __( 'Original', 'wpforo' ),
135
  'phrase_value' => __( 'Translation', 'wpforo' ),
136
+ 'package' => __( 'Package', 'wpforo' ),
137
+ 'langid' => __( 'Language', 'wpforo' ),
138
+ ];
139
  }
140
 
141
 
154
  * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
155
  **************************************************************************/
156
  function get_sortable_columns() {
157
+ return [
158
+ 'phraseid' => [ 'phraseid', false ], //true means it's already sorted
159
+ 'phrase_key' => [ 'phrase_key', false ],
160
+ 'phrase_value' => [ 'phrase_value', false ],
161
+ 'package' => [ 'package', false ],
162
+ 'langid' => [ 'langid', false ],
163
+ ];
164
  }
165
 
166
 
179
  * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
180
  **************************************************************************/
181
  function get_bulk_actions() {
182
+ return [ 'edit' => __( 'Edit', 'wpforo' ) ];
183
  }
184
 
185
 
190
  * $this->set_pagination_args(), although the following properties and methods
191
  * are frequently interacted with here...
192
  *
 
193
  * @uses $this->_column_headers
194
  * @uses $this->items
195
  * @uses $this->get_columns()
201
  /**
202
  * First, lets decide how many records per page to show
203
  */
204
+ $per_page = wpforo_get_option( 'count_per_page', 10 );
205
 
206
 
207
  /**
211
  * can be defined in another method (as we've done here) before being
212
  * used to build the value for our _column_headers property.
213
  */
214
+ $columns = $this->get_columns();
215
+ $hidden = [];
216
  $sortable = $this->get_sortable_columns();
217
 
218
 
222
  * 3 other arrays. One for all columns, one for hidden columns, and one
223
  * for sortable columns.
224
  */
225
+ $this->_column_headers = [ $columns, $hidden, $sortable ];
226
 
227
  /**
228
+ * REQUIRED. Now we can add our *sorted* data to the items' property, where
229
  * it can be used by the rest of the class.
230
  */
231
+ $args = [ 'langid' => $this->get_filter_by_langid_var() ];
232
+ if( $s = wpfval( $_REQUEST, 's' ) ) $args['include'] = WPF()->phrase->search( $s );
233
+ $orderby = wpfval( $_REQUEST, 'orderby' );
234
+ $order = strtoupper( wpfval( $_REQUEST, 'order' ) );
235
  $filter_by_package = $this->get_filter_by_package_var();
236
+ if( $filter_by_package !== - 1 ) $args['package'] = (array) $filter_by_package;
237
+ if( in_array( $order, [ 'ASC', 'DESC' ] ) ) $args['order'] = sanitize_text_field( $order );
238
+ if( array_key_exists( $orderby, $sortable ) ) $args['orderby'] = sanitize_text_field( $orderby );
239
 
240
+ $paged = $this->get_pagenum();
241
+ $args['offset'] = ( $paged - 1 ) * $per_page;
242
  $args['row_count'] = $per_page;
243
 
244
  $items_count = 0;
245
+ $this->items = ( isset( $args['include'] ) && empty( $args['include'] ) ? [] : WPF()->phrase->get_phrases( $args, $items_count, true ) );
246
 
247
  $this->wpfitems_count = $items_count;
248
 
249
+ $this->set_pagination_args( [
250
+ 'total_items' => $items_count, //WE have to calculate the total number of items
251
+ 'per_page' => $per_page, //WE have to determine how many items to show on a page
252
+ 'total_pages' => ceil( $items_count / $per_page ) //WE have to calculate the total number of pages
253
+ ] );
254
  }
255
 
256
+ public function get_filter_by_langid_var() {
257
+ $filter_by_langid = wpfval( $_REQUEST, 'filter_by_langid' );
258
+ if( ! is_null( $filter_by_langid ) && $filter_by_langid !== '-1' ) {
259
  $langid = $filter_by_langid;
260
+ } else {
261
+ $langid = WPF()->phrase->langid;
262
  }
263
+
264
+ return intval( $langid );
265
+ }
266
+
267
+ public function get_filter_by_package_var() {
268
+ $filter_by_package = wpfval( $_REQUEST, 'filter_by_package' );
269
+ if( ! is_null( $filter_by_package ) && $filter_by_package !== '-1' ) {
270
+ $package = $filter_by_package;
271
+ } else {
272
+ $package = - 1;
273
+ }
274
+
275
+ return $package;
276
+ }
277
+
278
+ public function packages_dropdown() { ?>
279
+ <label>
280
+ <select name="filter_by_package">
281
+ <option value="-1">-- <?php _e( 'filter by package', 'wpforo' ) ?> --</option>
282
+ <?php
283
+ if( $packages = WPF()->phrase->get_distinct_packages() ) {
284
+ $selected = $this->get_filter_by_package_var();
285
+ foreach( $packages as $package ) {
286
+ printf(
287
+ '<option value="%1$s" %2$s>%3$s</option>',
288
+ esc_attr( $package ),
289
+ ( $package === $selected ) ? 'selected' : '',
290
+ esc_html( $package )
291
+ );
292
+ }
293
  }
294
+ ?>
295
+ </select>
296
+ </label>
297
  <?php
298
  }
299
 
300
+ public function languages_dropdown() { ?>
301
+ <label>
302
+ <select name="filter_by_langid">
303
+ <?php WPF()->phrase->show_lang_list( $this->get_filter_by_langid_var() ); ?>
304
+ </select>
305
+ </label>
306
  <?php
307
  }
308
+ }
{wpf-admin/options-tabs → admin/listtables}/index.html RENAMED
File without changes
admin/pages/accesses.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if( ! defined( "ABSPATH" ) ) {
3
+ exit();
4
+ }
5
+ if( ! WPF()->usergroup->can( 'ms' ) ) exit;
6
+ $nocan = [
7
+ 'no_access' => [ 'et', 'dt', 'dot', 'er', 'dr', 'dor', 'l', 's', 'at', 'cot', 'p', 'op', 'vp', 'au', 'sv', 'mt', 'ccp', 'r', 'ct', 'cr', 'ocr', 'eot', 'eor', 'oat', 'osv', 'cvp', 'v', 'a', 'aot' ],
8
+ 'read_only' => [ 'et', 'dt', 'dot', 'er', 'dr', 'dor', 'l', 's', 'at', 'cot', 'p', 'op', 'vp', 'au', 'sv', 'mt', 'ccp', 'r' ],
9
+ 'standard' => [ 'et', 'dt', 'er', 'dr', 'at', 'cot', 'p', 'vp', 'au', 'sv', 'mt' ],
10
+ ];
11
+ $wpfaction = wpfval( $_GET, 'wpfaction' );
12
+ ?>
13
+
14
+ <div id="wpf-admin-wrap" class="wrap" style="margin-top: 0">
15
+ <h2 style="padding: 30px 0 0 0; line-height: 20px; margin-bottom: 15px;"><?php _e( 'Forum Accesses', 'wpforo' ); ?></h2>
16
+ <?php WPF()->notice->show() ?>
17
+ <!-- Option start -->
18
+ <div class="wpf-opt-row">
19
+ <div class="wpf-opt-intro">
20
+ <?php echo esc_html__( 'Forum Accesses are designed to do a Forum specific user permission control. These are set of permissions which are attached to certain Usergeoup in each forum. Thus users can have different permissions in different forums based on their Usergroup.', 'wpforo' ); ?>
21
+ <a href="https://wpforo.com/documentation/" title="<?php esc_attr_e( "Read the documentation", 'wpforo' ) ?>" target="_blank"><i class="far fa-question-circle"></i></a>
22
+ </div>
23
+ </div>
24
+ <!-- Option end -->
25
+
26
+ <?php if( ! $wpfaction ): ?>
27
+ <h2 style="margin-top:20px; margin-bottom:20px;">
28
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'accesses' ) . '&wpfaction=wpforo_access_save_form' ) ?>" class="add-new-h2">
29
+ <?php _e( 'Add New Forum Access', 'wpforo' ); ?>
30
+ </a>
31
+ </h2>
32
+ <table id="wpf-access-table" class="wp-list-table widefat fixed posts" style="border: 1px solid #ddd;">
33
+ <tbody id="wpf-access-list">
34
+ <?php foreach( WPF()->perm->get_accesses() as $access ) : ?>
35
+ <tr id="post-2" class="post-1 type-post status-publish format-standard hentry category-uncategorized alternate iedit author-self">
36
+ <td class="post-title page-title column-title" style="border-bottom:1px dotted #CCCCCC; padding-left:20px;">
37
+ <strong class="row-title">
38
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'accesses' ) . '&wpfaction=wpforo_access_save_form&accessid=' . intval( $access['accessid'] ) ) ?>" title="<?php echo esc_attr( $access['title'] ) ?>">
39
+ <?php _e( $access['title'], 'wpforo' ) ?>
40
+ </a>
41
+ </strong>
42
+ <p class="wpf-info">
43
+ <?php if( $access['access'] === 'read_only' ) {
44
+ _e( 'This access is usually used for ', 'wpforo' );
45
+ echo '<span style="color:#F45B00"><b>';
46
+ _e( 'Guests', 'wpforo' );
47
+ echo '</b></span> ';
48
+ _e( 'usergroup', 'wpforo' );
49
+ } ?>
50
+ <?php if( $access['access'] === 'standard' ) {
51
+ _e( 'This access is usually used for ', 'wpforo' );
52
+ echo '<span style="color:#F45B00"><b>';
53
+ _e( 'Registered', 'wpforo' );
54
+ echo '</b></span> ';
55
+ _e( 'usergroup', 'wpforo' );
56
+ } ?>
57
+ <?php if( $access['access'] === 'full' ) {
58
+ _e( 'This access is usually used for ', 'wpforo' );
59
+ echo '<span style="color:#F45B00"><b>';
60
+ _e( 'Admin', 'wpforo' );
61
+ echo '</b></span> ';
62
+ _e( 'usergroup', 'wpforo' );
63
+ } ?>
64
+ </p>
65
+ <div class="row-actions">
66
+ <span class="edit">
67
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'accesses' ) . '&wpfaction=wpforo_access_save_form&accessid=' . intval( $access['accessid'] ) ) ?>">
68
+ <?php _e( 'edit', 'wpforo' ); ?>
69
+ </a>
70
+ </span>
71
+ <?php if( $access['accessid'] > 5 ): ?>
72
+ <span class="trash"> |
73
+ <a class="submitdelete" href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'accesses' ) . '&wpfaction=access_delete&accessid=' . intval( $access['accessid'] ) ), 'wpforo-access-delete-' . intval( $access['accessid'] ) ) ?>"
74
+ onclick="return confirm('<?php _e( 'Are you sure you want to remove this access set? Usergroups which attached to this access will lost all forum permissions.' ) ?>')">
75
+ <?php _e( 'delete', 'wpforo' ) ?>
76
+ </a>
77
+ </span>
78
+ <?php endif; ?>
79
+ </div>
80
+ </td>
81
+ </tr>
82
+ <?php endforeach ?>
83
+ </tbody>
84
+ </table>
85
+ <?php elseif( $wpfaction === 'wpforo_access_save_form' ) :
86
+ $access = WPF()->perm->get_access( wpfval( $_GET, 'accessid' ) );
87
+ $disabled_cans = (array) wpfval( $nocan, $access['access'] ); ?>
88
+ <div class="form-wrap">
89
+ <div class="form-wrap">
90
+ <form id="add_access" method="POST">
91
+ <?php
92
+ if( $access['accessid'] ) {
93
+ wp_nonce_field( 'wpforo-access-edit' ); ?>
94
+ <input type="hidden" name="wpfaction" value="access_edit">
95
+ <?php
96
+ } else {
97
+ wp_nonce_field( 'wpforo-access-add' ); ?>
98
+ <input type="hidden" name="wpfaction" value="access_add">
99
+ <?php
100
+ }
101
+ ?>
102
+ <input type="hidden" name="access[accessid]" value="<?php echo esc_attr( $access['accessid'] ) ?>">
103
+ <input type="hidden" name="access[access]" value="<?php echo esc_attr( $access['access'] ) ?>">
104
+ <label for="access-name" class="wpf-label-big"><?php _e( 'Access name', 'wpforo' ); ?></label>
105
+ <input id="access-name" name="access[title]" type="text" value="<?php echo esc_attr( $access['title'] ) ?>" size="40" required style="background:#FDFDFD; width:30%; min-width:320px;">
106
+ <p>&nbsp;</p>
107
+ <?php $n = 0;
108
+ foreach( WPF()->perm->cans as $can => $name ) :
109
+ $disabled = in_array( $can, $disabled_cans );
110
+ if( ! ( $n % 4 ) ) : ?>
111
+ </table>
112
+ <table class="wpf-table-box-left" style="margin-right:10px; margin-bottom:10px; width:48%; min-width: 250px;">
113
+ <?php endif; ?>
114
+ <tr>
115
+ <th class="wpf-dw-td-nowrap">
116
+ <label class="wpf-td-label" for="wpf-can-<?php echo esc_attr( $can ) ?>" <?php if( $disabled ) echo ' style="color: #aaa;" ' ?>>
117
+ <?php echo esc_html( __( $name, 'wpforo' ) ) ?>
118
+ </label>
119
+ </th>
120
+ <td class="wpf-dw-td-value" style="text-align:center;">
121
+ <input id="wpf-can-<?php echo esc_attr( $can ) ?>" type="checkbox" value="1" name="access[cans][<?php echo esc_attr( $can ) ?>]" <?php echo $access['cans'][ $can ] ? ' checked ' : '' ?> <?php if( $disabled ) echo ' disabled ' ?>>
122
+ </td>
123
+ </tr>
124
+ <?php $n ++;
125
+ endforeach ?>
126
+ </table>
127
+ <div class="clear"></div>
128
+ <div class="wpf-opt-row" style="flex-wrap: wrap;">
129
+ <div>&nbsp;</div>
130
+ <div>
131
+ <input style="float: right; padding-left: 21px; padding-right: 21px;" type="submit" class="button button-primary" value="<?php esc_attr_e( "Save", "wpforo" ); ?>"/>
132
+ </div>
133
+ </div>
134
+ </form>
135
+ </div>
136
+ </div>
137
+ <?php endif ?>
138
+
139
+ </div>
admin/pages/addons.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! current_user_can( 'administrator' ) ) exit;
5
+
6
+ WPF()->notice->refreshAddonPage();
7
+ WPF()->notice->dismissAddonNoteOnPage();
8
+
9
+ ?>
10
+
11
+ <div id="wpf-admin-wrap" class="wrap wpforo-addons">
12
+ <div style="float:left; width:50px; height:55px; margin:10px 10px 20px 0;">
13
+ <img src="<?php echo WPFORO_URL ?>/assets/images/wpforo-40.png" style="border:2px solid #fff;" alt="wpforo">
14
+ </div>
15
+ <h1 style="padding-bottom:20px; padding-top:15px;"><?php _e( 'wpForo Addons', 'wpforo' ); ?></h1>
16
+ <br style="clear:both">
17
+ <table class="widefat" width="100%" cellspacing="1" border="0">
18
+ <tbody>
19
+ <tr>
20
+ <td style="padding:10px 10px 0px 10px;" valign="top">
21
+ <table width="100%" cellspacing="1" border="0">
22
+ <thead>
23
+ <tr>
24
+ <th style="font-size:16px; padding-bottom:15px;"><strong><?php _e( 'wpForo Addons', 'wpforo' ); ?></strong></th>
25
+ <th style="font-size:16px; padding-bottom:15px; width:205px; text-align:center; border-bottom:1px solid #008EC2;"><a href="http://gvectors.com/forum/" style="color:#008EC2; overflow:hidden; outline:none;" target="_blank">Addons Support Forum</a></th>
26
+ </tr>
27
+ </thead>
28
+ <tbody>
29
+ <tr valign="top">
30
+ <td colspan="2" style="background:#FFF; text-align:left; font-size:13px;">
31
+ <?php _e(
32
+ 'All wpForo addons are being developed by wpForo developers at gVectors Team. Addon prices also include a small donation to the hard work wpForo developers do for free. When you buy an addon, you also donate the free wpForo development and support. Addons are the only incoming source for wpForo developers. wpForo is a premium forum plugin which will always be available for free. There will never be paid and pro versions of this forum board. We have another dozens of awesome features in our to-do list which will also be added for free in future releases. So the free wpForo development always stays on the first priority and wpForo is being extended with new free functions and features even faster than before.',
33
+ 'wpforo'
34
+ ); ?>
35
+ <br>
36
+ <p style="font-size:10px; color:#B1B1B1; font-style:italic; text-align:right; line-height:12px; padding-top:6px; margin:0px;">
37
+ <?php _e( 'Thank you!<br> Sincerely yours,<br> gVectors Team', 'wpforo' ); ?>&nbsp; </p>
38
+ </td>
39
+ </tr>
40
+ </tbody>
41
+ </table>
42
+ </td>
43
+ </tr>
44
+ </tbody>
45
+ </table>
46
+ <br style="clear:both">
47
+ <div class="wpforo-addons-wrapper">
48
+ <?php
49
+ foreach( wpforo_get_addons_info() as $key => $addon ) {
50
+ $installed = class_exists( $addon['class'] );
51
+ ?>
52
+ <div class="wpforo-addon-block">
53
+ <div id="wpforo-addon-<?php echo $key ?>" class="addon-thumb" style="background:url(<?php echo ( $installed ) ? str_replace( '.png', '-off.png', $addon['thumb'] ) : $addon['thumb']; ?>) top center no-repeat;">
54
+ &nbsp;
55
+ </div>
56
+ <div class="contenthover">
57
+ <div class="addon-isactive">
58
+ <?php if( $installed ) { ?>
59
+ <div class="note-installed"><?php _e( 'Installed', 'default' ); ?></div>
60
+ <?php } else { ?>
61
+ <h3 style="font-weight:normal; font-size:22px; line-height: 25px; margin-bottom:2px; text-shadow: 0 0 2px #999;"><?php echo $addon['title'] ?></h3>
62
+ <ul>
63
+ <li style="line-height:16px;"><?php _e( 'Version', 'default' ); ?>: <?php echo $addon['version']; ?></li>
64
+ <li style="line-height:16px;">wpForo: <?php _e( 'at least', 'default' ); ?> <?php echo $addon['requires']; ?></li>
65
+ </ul>
66
+ <a class="button button-primary addon-button" href="<?php echo $addon['url']; ?>" target="_blank" style="font-size:14px;"><?php echo __( 'Details | Buy', 'wpforo' ); ?></a>
67
+ <?php } ?>
68
+ </div>
69
+ </div>
70
+ <div style="clear:both"></div>
71
+ <div class="addon-info" style="<?php if( $installed ) echo 'background-color:#bbbbbb'; ?>">
72
+ <a href="<?php echo $addon['url']; ?>" target="_blank" title="<?php _e( 'More information about', 'default' ); ?> <?php echo $addon['title'] ?> add-on &raquo;">
73
+ <p class="addon-title"><?php echo $addon['title']; ?></p>
74
+ </a>
75
+ <div class="addon-desc"><?php echo $addon['desc']; ?></div>
76
+ </div>
77
+ </div>
78
+ <script language="javascript">jQuery(document).ready(function ($) { $('#wpforo-addon-<?php echo $key ?>').contenthover({ overlay_width: 290, overlay_height:<?php echo ( $installed ) ? '100' : '180'; ?>, effect: 'slide', slide_direction: 'right', overlay_x_position: 'right', overlay_y_position: 'center', overlay_background: '#e5e5e5', overlay_opacity: 0.9 }) })</script>
79
+ <?php } ?>
80
+ </div>
81
+ <div style="clear:both;"></div>
82
+ <h3>&nbsp;</h3>
83
+ <hr/>
84
+ </div>
admin/pages/board.php ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if( wpfkey( $_GET, 'boardid' ) ) {
3
+ $board = WPF()->board->get_current();
4
+ } else {
5
+ $board = WPF()->board->decode( [] );
6
+ $board['title'] = $board['slug'] = '';
7
+ }
8
+ $action = wpfval( $_GET, 'wpfaction' );
9
+ $boardid = (int) wpfval( $_GET, 'boardid' );
10
+ ?>
11
+
12
+ <div id="icon-edit" class="icon32 icon32-posts-post"></div>
13
+
14
+ <div id="wpf-admin-wrap" class="wrap" style="padding-right: 50px;">
15
+ <h2 style="padding:30px 0 10px; line-height: 20px;">
16
+ <?php _e( 'Boards', 'wpforo' ); ?> &nbsp;
17
+ <a href="<?php echo admin_url( 'admin.php?page=wpforo-boards&wpfaction=wpforo_board_save_form' ) ?>" class="add-new-h2"><?php _e( 'Add New', 'wpforo' ); ?></a>
18
+ </h2>
19
+ <?php WPF()->notice->show();
20
+ if( $action ) : ?>
21
+
22
+ <div style="background: #fff; width: 100%; font-size: 16px; padding: 20px; margin-top: 20px; line-height: 24px; box-sizing: border-box;">
23
+ <div style="font-size: 20px; font-weight: 500; margin-bottom: 5px; margin-top: 0">
24
+ <svg style="height: 33px; vertical-align: text-bottom; margin-bottom: -3px; margin-right: 3px; fill: #f07d02;" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:none;}</style></defs><title/><g data-name="Layer 2" id="Layer_2"><path d="M22.7,28H9.3a6.25,6.25,0,0,1-5.47-3.15,6.15,6.15,0,0,1,0-6.22L10.56,7.12a6.3,6.3,0,0,1,10.88,0l6.71,11.51h0a6.15,6.15,0,0,1,0,6.22A6.25,6.25,0,0,1,22.7,28ZM16,6a4.24,4.24,0,0,0-3.71,2.12L5.58,19.64a4.15,4.15,0,0,0,0,4.21A4.23,4.23,0,0,0,9.3,26H22.7a4.23,4.23,0,0,0,3.73-2.15,4.15,4.15,0,0,0,0-4.21L19.71,8.12A4.24,4.24,0,0,0,16,6Z"/><path class="cls-1" d="M16,12a.54.54,0,0,0-.44.22.52.52,0,0,0-.1.48L16,14.88l.54-2.18a.52.52,0,0,0-.1-.48A.54.54,0,0,0,16,12Z"/><path d="M18,11a2.56,2.56,0,0,0-4,0,2.5,2.5,0,0,0-.46,2.19L15,19.24a1,1,0,0,0,1.94,0l1.51-6.06A2.5,2.5,0,0,0,18,11ZM16.54,12.7,16,14.88l-.54-2.18a.52.52,0,0,1,.1-.48.55.55,0,0,1,.88,0A.52.52,0,0,1,16.54,12.7Z"/><circle cx="16" cy="22.5" r="1.5"/></g><g id="frame"><rect class="cls-1" height="32" width="32"/></g></svg>
25
+ <span style="color: #d5740f"><?php _e( 'Boards are not Forum Categories', 'wpforo' ) ?></span>
26
+ </div>
27
+ <div style="padding: 5px 2px 0 2px; line-height: 23px;">
28
+ <?php if( ! $board['slug'] ) printf( '<span style="font-weight: bold;">%1$s</span>', __( 'You\'re about to create a new separate discussion board in your website.', 'wpforo' ) ); ?>
29
+ <?php _e( 'Please note that boards are separate forum pages, you can use them if you want to have more than one forum in your website. Also, you can use boards to create separate forums for different languages. If you want to add a new category or forum, please use the Forums menu under the menu section of existing discussion boards.', 'wpforo' ) ?>
30
+ </div>
31
+ </div>
32
+
33
+ <form method="post">
34
+
35
+ <div class="wpf-board">
36
+
37
+ <div class="wpf-board-left">
38
+
39
+ <?php if( wpfkey( $_GET, 'boardid' ) ) : ?>
40
+ <?php wp_nonce_field( 'wpforo-board-edit' ); ?>
41
+ <input type="hidden" name="wpfaction" value="board_edit">
42
+ <?php else : ?>
43
+ <?php wp_nonce_field( 'wpforo-board-add' ); ?>
44
+ <input type="hidden" name="wpfaction" value="board_add">
45
+ <?php endif; ?>
46
+ <input type="hidden" name="board[boardid]" value="<?php echo $board['boardid'] ?>">
47
+
48
+
49
+ <div class="wpf-board-option">
50
+ <label for="wpf-board-settings-title"><?php _e( 'Board Title', 'wpforo' ); ?>*</label>
51
+ <div class="wpf-board-field">
52
+ <input type="text" name="board[settings][title]" id="wpf-board-settings-title" value="<?php echo $board['settings']['title'] ?>" style="font-size: 22px; background: #fafafa;" required>
53
+ </div>
54
+ </div>
55
+
56
+ <div class="wpf-board-option">
57
+ <label for="wpf-board-title"><?php _e('Board Label (used in menu, max 12 characters)', 'wpforo'); ?>*</label>
58
+ <div class="wpf-board-field">
59
+ <input type="text" name="board[title]" id="wpf-board-title" value="<?php echo $board['title'] ?>" maxlength="12" placeholder="<?php _e('Short title of this board, max 12 characters', 'wpforo') ?>" required>
60
+ </div>
61
+ </div>
62
+
63
+ <div class="wpf-board-option">
64
+ <label for="wpf-board-slug"><?php _e( 'Board Slug', 'wpforo' ); ?>* </label>
65
+ <div class="wpfb-desc" style="padding: 5px 0 0;"><?php _e( 'The "slug" is the URL-friendly version of the board name. It is usually all lowercase and contains only letters, numbers, and hyphens.', 'wpforo' ) ?></div>
66
+ <div class="wpf-board-field">
67
+ <input id="wpf-board-slug" type="text" name="board[slug]" value="<?php echo $board['slug'] ?>" required>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="wpf-board-option">
72
+ <label for="wpf-board-settings-desc"><?php _e( 'Board Description', 'wpforo' ); ?></label>
73
+ <div class="wpf-board-field">
74
+ <textarea name="" id="wpf-board-settings-desc" rows="4"><?php echo $board['settings']['desc'] ?></textarea>
75
+ </div>
76
+ </div>
77
+
78
+ <div class="wpf-board-box">
79
+ <div class="wpf-board-locale" style="">
80
+ <label for="wpf-board-locale">
81
+ <?php _e( 'Language', 'wpforo' ); ?>
82
+ <span class="wpfb-desc"><?php _e('If you have multiple languages in your website and want to have forums with different languages, then make sure the language of this forum board is set correctly, otherwise just leave the default language.', '') ?></span>
83
+ </label>
84
+ <?php wp_dropdown_languages( [ 'id' => 'wpf-board-locale', 'name' => 'board[locale]', 'selected' => ( $board['locale'] ? : get_locale() ) ] ); ?>
85
+ </div>
86
+ <div class="wpf-board-pageid">
87
+ <label for="wpf-board-pageid"><?php _e( 'Page ID', 'wpforo' ); ?> <span class="wpfb-desc"><?php _e('The new forum page will be created automatically with the next autoincrement page ID, you do not need to modify it when you create a new forum board. Only change this ID if you have already created a new page for this forum board.', '') ?></span></label>
88
+ <input id="wpf-board-pageid" type="number" name="board[pageid]" value="<?php echo $board['pageid'] ?>">
89
+ </div>
90
+ </div>
91
+
92
+ <div class="wpf-board-modules">
93
+
94
+ <div class="wpf-module-type"><?php _e('Modules', 'wpforo') ?></div>
95
+
96
+ <?php foreach( wpforo_get_modules_info(false) as $key => $module ) : ?>
97
+ <div class="wpf-board-module">
98
+ <div class="wpf-module-head">
99
+ <?php if( strpos($module['thumb'], '<svg') === FALSE ): ?>
100
+ <?php $src = ( $module['thumb'] ) ? $module['thumb'] : WPFORO_URL . "/assets/images/dashboard/addon.png" ?>
101
+ <img src="<?php echo esc_url_raw($src) ?>" style="height: 60px;">
102
+ <?php else: ?>
103
+ <?php echo $module['thumb'] ?>
104
+ <?php endif; ?>
105
+ </div>
106
+ <div>
107
+ <div class="wpf-module-title"><?php echo $module['title'] ?></div>
108
+ <div class="wpf-switch-field">
109
+ <input type="radio" value="1" name="board[modules][<?php echo $key ?>]" id="wpf-board-<?php echo $key ?>_1" <?php wpfo_check( (bool) wpfval( $board['modules'], $key ), true ); ?>><label for="wpf-board-<?php echo $key ?>_1"><?php _e( 'Enabled', 'wpforo' ) ?></label> &nbsp;
110
+ <input type="radio" value="0" name="board[modules][<?php echo $key ?>]" id="wpf-board-<?php echo $key ?>_0" <?php wpfo_check( (bool) wpfval( $board['modules'], $key ), false ); ?>><label for="wpf-board-<?php echo $key ?>_0"><?php _e( 'Disabled', 'wpforo' ) ?></label>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ <?php endforeach; ?>
115
+
116
+ <div class="wpf-module-type" style="margin-top: 15px;"><?php _e('Addons', 'wpforo') ?></div>
117
+
118
+ <?php foreach( wpforo_get_addons_info(false) as $key => $addon ) : ?>
119
+ <div class="wpf-board-module">
120
+ <div class="wpf-module-head">
121
+ <?php if( strpos($addon['thumb'], '<svg') === FALSE ): ?>
122
+ <?php $src = $addon['thumb'] ?: WPFORO_URL . "/assets/images/dashboard/addon.png" ?>
123
+ <img src="<?php echo esc_url_raw($src) ?>" style="height: 55px;">
124
+ <?php else: ?>
125
+ <?php echo $addon['thumb'] ?>
126
+ <?php endif; ?>
127
+ </div>
128
+ <div>
129
+ <div class="wpf-module-title"><?php echo $addon['title'] ?></div>
130
+ <?php if( class_exists( $addon['class'] ) ) : ?>
131
+ <div class="wpf-switch-field">
132
+ <input type="radio" value="1" name="board[modules][<?php echo $key ?>]" id="wpf-board-<?php echo $key ?>_1" <?php wpfo_check( (bool) wpfval( $board['modules'], $key ), true ); ?>><label for="wpf-board-<?php echo $key ?>_1"><?php _e( 'Enabled', 'wpforo' ) ?></label> &nbsp;
133
+ <input type="radio" value="0" name="board[modules][<?php echo $key ?>]" id="wpf-board-<?php echo $key ?>_0" <?php wpfo_check( (bool) wpfval( $board['modules'], $key ), false ); ?>><label for="wpf-board-<?php echo $key ?>_0"><?php _e( 'Disabled', 'wpforo' ) ?></label>
134
+ </div>
135
+ <?php else :
136
+ printf(
137
+ '<a class="button" href="%1$s" title="%2$s" target="_blank">%3$s</a>',
138
+ $addon['url'],
139
+ __('Go to the addon page', 'wpforo'),
140
+ __('Get Addon','wpforo')
141
+ );
142
+ ?>
143
+ <?php endif; ?>
144
+ </div>
145
+ </div>
146
+ <?php endforeach; ?>
147
+ </div>
148
+
149
+ <div class="wpf-board-box">
150
+ <div class="wpf-board-is_standalone">
151
+ <label>
152
+ <?php _e( 'Turn WordPress to this forum board', 'wpforo' ) ?> <a href="https://wpforo.com/docs/wpforo-v2/getting-started/forum-page/turn-wordpress-to-wpforo/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"><i class="far fa-question-circle"></i></a>
153
+ <p class="wpf-info"><?php _e( 'This option will disable WordPress on front-end. Only forum pages and excluded post/pages will be available. wpForo will look like as a stand-alone forum.', 'wpforo' ) ?></p>
154
+ </label>
155
+ <div class="wpf-switch-field">
156
+ <input type="radio" value="1" name="board[is_standalone]" id="wpf-board-is_standalone_1" <?php wpfo_check( $board['is_standalone'], true ); ?>><label for="wpf-board-is_standalone_1"><?php _e( 'Enabled', 'wpforo' ) ?></label> &nbsp;
157
+ <input type="radio" value="0" name="board[is_standalone]" id="wpf-board-is_standalone_0" <?php wpfo_check( $board['is_standalone'], false ); ?>><label for="wpf-board-is_standalone_0"><?php _e( 'Disabled', 'wpforo' ) ?></label>
158
+ </div>
159
+ </div>
160
+ <div class="wpf-board-excld_urls">
161
+ <label for="wpf-board-excld_urls"><b style="font-weight: bold;">* <?php _e( 'Exclude page URLs', 'wpforo' ) ?></b> <span class="wpf-info">(<?php _e( 'one URL per line', 'wpforo' ) ?>)</span></label>
162
+ <textarea id="wpf-board-excld_urls" style="font-size: 12px; width: 100%;" rows="5" name="wpforo_excld_urls" placeholder="<?php echo esc_url( home_url( '/' ) ) ?>sample-page/&#10;<?php echo esc_url( home_url( '/' ) ) ?>hello-world/&#10;<?php echo esc_url( home_url( '/' ) ) ?>category/*&#10; ..."
163
+ ><?php echo esc_textarea( implode( PHP_EOL, $board['excld_urls'] ) ) ?></textarea>
164
+ </div>
165
+ </div>
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+
175
+
176
+
177
+
178
+ </div><!-- wpf-board-left END -->
179
+
180
+ <div class="wpf-board-right">
181
+
182
+ <div class="wpf-board-side-box">
183
+ <label><?php _e( 'Status', 'wpforo' ); ?></label>
184
+ <div class="wpf-switch-field">
185
+ <input type="radio" value="1" name="board[status]" id="wpf-board-status_1" <?php wpfo_check( $board['status'], true ); ?>><label for="wpf-board-status_1"><?php _e( 'Enabled', 'wpforo' ) ?></label> &nbsp;
186
+ <input type="radio" value="0" name="board[status]" id="wpf-board-status_0" <?php wpfo_check( $board['status'], false ); ?>><label for="wpf-board-status_0"><?php _e( 'Disabled', 'wpforo' ) ?></label>
187
+ </div>
188
+ </div>
189
+
190
+ <div class="wpf-board-submit">
191
+ <input type="submit" value="<?php _e( 'Save', 'wpforo' ) ?>" class="button button-primary">
192
+ </div>
193
+
194
+ </div><!-- wpf-board-right END -->
195
+
196
+ </div>
197
+
198
+ </form>
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+ <?php else : ?>
210
+ <!-- Now we can render the completed list table -->
211
+ <?php WPF()->board->list_table->display() ?>
212
+ <?php endif; ?>
213
+ </div>
admin/pages/dashboard.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ ?>
5
+
6
+ <div id="wpf-admin-wrap" class="wrap">
7
+ <h1 style="padding:30px 10px 10px;"><?php _e( 'Forum Dashboard', 'wpforo' ); ?></h1>
8
+ <?php WPF()->notice->show() ?>
9
+
10
+ <div id="dashboard-widgets-wrap">
11
+ <div class="metabox-holder" id="dashboard-widgets">
12
+
13
+ <?php if( current_user_can( 'administrator' ) || current_user_can( 'editor' ) || current_user_can( 'author' ) ): ?>
14
+
15
+ <div class="postbox-container" style="width: 100%;">
16
+ <div class="wpf-box-wrap">
17
+
18
+ <div class="postbox wpf-dash-box" id="wpforo_dashboard_widget_statistic">
19
+ <h2 class="wpf-box-header"><span><?php _e( 'Board Statistic', 'wpforo' ); ?></span></h2>
20
+ <div class="inside">
21
+ <div class="main">
22
+ <table style="width:98%; margin:0 auto; text-align:left;">
23
+ <?php $statistic = WPF()->statistic(); ?>
24
+ <tr class="wpf-dw-tr">
25
+ <td class="wpf-dw-td"><?php _e( 'Forums', 'wpforo' ); ?></td>
26
+ <td class="wpf-dw-td-value"><?php echo intval( $statistic['forums'] ) ?></td>
27
+ </tr>
28
+ <tr class="wpf-dw-tr">
29
+ <td class="wpf-dw-td"><?php _e( 'Topics', 'wpforo' ); ?></td>
30
+ <td class="wpf-dw-td-value"><?php echo intval( $statistic['topics'] ) ?></td>
31
+ </tr>
32
+ <tr class="wpf-dw-tr">
33
+ <td class="wpf-dw-td"><?php _e( 'Posts', 'wpforo' ); ?></td>
34
+ <td class="wpf-dw-td-value"><?php echo intval( $statistic['posts'] ) ?></td>
35
+ </tr>
36
+ <tr class="wpf-dw-tr">
37
+ <td class="wpf-dw-td"><?php _e( 'Members', 'wpforo' ); ?></td>
38
+ <td class="wpf-dw-td-value"><?php echo intval( $statistic['members'] ) ?></td>
39
+ </tr>
40
+ <tr class="wpf-dw-tr">
41
+ <td class="wpf-dw-td"><?php _e( 'Members Online', 'wpforo' ); ?></td>
42
+ <td class="wpf-dw-td-value"><?php echo intval( $statistic['online_members_count'] ) ?></td>
43
+ </tr>
44
+ <?php
45
+ $size_avatar = wpforo_dir_size( WPF()->folders['avatars']['dir'] );
46
+ $size_da = wpforo_dir_size( WPF()->folders['default_attachments']['dir'] );
47
+ $size_aa = wpforo_dir_size( WPF()->folders['attachments']['dir'] );
48
+ ?>
49
+ <tr class="wpf-dw-tr">
50
+ <td class="wpf-dw-td"><?php _e( 'Avatars Size', 'wpforo' ); ?></td>
51
+ <td class="wpf-dw-td-value"><?php echo wpforo_human_filesize( $size_avatar ); ?></td>
52
+ </tr>
53
+ <tr class="wpf-dw-tr">
54
+ <td class="wpf-dw-td"><?php _e( 'Default Attachments Size', 'wpforo' ); ?></td>
55
+ <td class="wpf-dw-td-value"><?php echo wpforo_human_filesize( $size_da ); ?></td>
56
+ </tr>
57
+ <?php if( isset( $statistic['attachments'] ) && $statistic['attachment_sizes'] ) : ?>
58
+ <tr class="wpf-dw-tr">
59
+ <td class="wpf-dw-td"><?php _e( 'Advanced Attachments', 'wpforo' ); ?></td>
60
+ <td class="wpf-dw-td-value"><?php echo esc_html( $statistic['attachments'] ) ?><?php _e( 'file(s)', 'wpforo' ); ?></td>
61
+ </tr>
62
+ <tr class="wpf-dw-tr">
63
+ <td class="wpf-dw-td"><?php _e( 'Advanced Attachments Size', 'wpforo' ); ?></td>
64
+ <td class="wpf-dw-td-value"><?php echo wpforo_human_filesize( $size_aa ); ?></td>
65
+ </tr>
66
+ <?php endif ?>
67
+ <tr class="wpf-dw-tr">
68
+ <td class="wpf-dw-td"><?php _e( 'Total Size', 'wpforo' ); ?></td>
69
+ <td class="wpf-dw-td-value">
70
+ <strong style="font-size:14px;">
71
+ <?php
72
+ $total = $size_avatar + $size_da + $size_aa;
73
+ echo wpforo_human_filesize( $total );
74
+ ?>
75
+ </strong>
76
+ </td>
77
+ </tr>
78
+ </table>
79
+ </div>
80
+ </div>
81
+ </div><!-- widget / postbox -->
82
+
83
+ <?php if( wpforo_current_user_is( 'admin' ) ) : ?>
84
+ <div class="postbox wpf-dash-box" id="wpforo_dashboard_widget_0">
85
+ <h2 class="wpf-box-header">
86
+ <span><?php _e( 'Forum Maintenance', 'wpforo' ); ?></span>
87
+ </h2>
88
+ <p class="wpf-info" style="padding:10px;"><?php _e( "This process may take a few seconds or dozens of minutes, please be patient and don't close this page. If you got 500 Server Error please don't worry, the data updating process is still working in MySQL server.", 'wpforo' ); ?></p>
89
+ <div class="inside">
90
+ <div class="main">
91
+ <div style="width:100%; padding:7px 0;">
92
+ <?php
93
+ $reset_cache = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=reset_all_caches' ), 'wpforo_reset_cache' );
94
+ $clean_up = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=clean_up' ), 'wpforo_clean_up' );
95
+ $reset_forums_stat_url = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=reset_forums_stats' ), 'wpforo_reset_forums_stat' );
96
+ $reset_topics_stat_url = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=reset_topics_stats' ), 'wpforo_reset_topics_stat' );
97
+ $reset_phrase_cache = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=reset_phrase_cache' ), 'wpforo_reset_phrase_cache' );
98
+ $recrawl_phrases = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=recrawl_phrases' ), 'wpforo_recrawl_phrases' );
99
+ $rebuild_threads = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=rebuild_threads' ), 'wpforo_rebuild_threads' );
100
+ ?>
101
+ <a href="<?php echo esc_url( $reset_cache ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Delete All Caches', 'wpforo' ); ?></a>&nbsp;
102
+ <a href="<?php echo esc_url( $clean_up ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Clean Up', 'wpforo' ); ?></a>&nbsp;
103
+ <a href="<?php echo esc_url( $reset_forums_stat_url ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Update Forums Statistic', 'wpforo' ); ?></a>&nbsp;
104
+ <a href="<?php echo esc_url( $reset_topics_stat_url ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Update Topics Statistic', 'wpforo' ); ?></a>&nbsp;
105
+ <a href="<?php echo esc_url( $recrawl_phrases ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Rebuild Phrases', 'wpforo' ); ?></a>&nbsp;
106
+ <a href="<?php echo esc_url( $reset_phrase_cache ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Delete Phrase Cache', 'wpforo' ); ?></a>&nbsp;
107
+ <a href="<?php echo esc_url( $rebuild_threads ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Rebuild Threads', 'wpforo' ); ?></a>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div><!-- widget / postbox -->
112
+ <?php endif ?>
113
+
114
+ <?php do_action( 'wpforo_dashboard_widgets_col2', WPF() ); ?>
115
+
116
+ </div><!-- normal-sortables -->
117
+ </div><!-- wpforo_postbox_container -->
118
+ <?php endif; ?>
119
+
120
+ <div class="postbox-container" id="postbox-container-3">
121
+ <div class="wpf-box-wrap">
122
+
123
+ <?php do_action( 'wpforo_dashboard_widgets_col3', WPF() ); ?>
124
+
125
+ </div><!-- normal-sortables -->
126
+ </div><!-- wpforo_postbox_container -->
127
+
128
+ </div><!-- dashboard-widgets -->
129
+ </div><!-- dashboard-widgets-wrap -->
130
+
131
+ </div><!-- wpwrap -->
132
+
admin/pages/forum.php ADDED
@@ -0,0 +1,491 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! WPF()->usergroup->can_manage_forum() ) exit;
5
+
6
+ $action = wpfval( $_GET, 'action' );
7
+ ?>
8
+
9
+ <!-- Screen Options -->
10
+ <?php if( in_array( $action, [ 'add', 'edit' ] ) ) : ?>
11
+
12
+ <div id="screen-meta" class="metabox-prefs" style="display: none; ">
13
+ <div id="screen-options-wrap" class="hidden" tabindex="-1" aria-label="Screen Options Tab" style="display: none; ">
14
+ <form id="adv-settings" action="" method="POST">
15
+ <h5><?php _e( 'Show on screen', 'wpforo' ); ?></h5>
16
+ <div class="metabox-prefs">
17
+ <label for="forum_cat-hide"><input class="hide-postbox-tog" name="forum_cat-hide" type="checkbox" id="forum_cat-hide" value="forum_cat" checked="checked"><?php _e( 'Forum Options', 'wpforo' ); ?></label>
18
+ <label for="forum_permissions-hide"><input class="hide-postbox-tog" name="forum_permissions-hide" type="checkbox" id="forum_permissions-hide" value="forum_permissions" checked="checked"><?php _e( 'Permissions', 'wpforo' ); ?></label>
19
+ <label for="forum_slug-hide"><input class="hide-postbox-tog" name="forum_slug-hide" type="checkbox" id="forum_slug-hide" value="forum_slug" checked="checked"><?php _e( 'Slug', 'wpforo' ); ?></label>
20
+ <label for="forum_meta-hide"><input class="hide-postbox-tog" name="forum_meta-hide" type="checkbox" id="forum_meta-hide" value="forum_meta" checked="checked"><?php _e( 'Forum Meta', 'wpforo' ); ?></label>
21
+ <br class="clear">
22
+ </div>
23
+ <h5 class="screen-layout"><?php _e( 'Screen Layout', 'wpforo' ); ?></h5>
24
+ <div class="columns-prefs"><?php _e( 'Number of Columns', 'wpforo' ); ?>:
25
+ <label class="columns-prefs-1"><input type="radio" name="screen_columns" value="1">1</label>
26
+ <label class="columns-prefs-2"><input type="radio" name="screen_columns" value="2" checked="checked">2</label>
27
+ </div>
28
+ </form>
29
+ </div>
30
+ </div>
31
+
32
+ <div id="screen-meta-links">
33
+ <div id="screen-options-link-wrap" class="hide-if-no-js screen-meta-toggle" style="">
34
+ <button aria-expanded="true" aria-controls="screen-options-wrap" class="button show-settings screen-meta-active" id="show-settings-link" type="button"><?php _e( 'Screen Options', 'wpforo' ); ?></button>
35
+ </div>
36
+ </div>
37
+
38
+ <?php endif; ?>
39
+ <!-- end Screen Options -->
40
+
41
+ <div id="icon-edit" class="icon32 icon32-posts-post"></div>
42
+ <div id="wpf-admin-wrap" class="wrap">
43
+
44
+ <h2 style="padding:30px 0 10px; line-height: 20px;">
45
+ <?php _e( 'Categories and Forums', 'wpforo' ); ?> &nbsp;
46
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&action=add' ) ?>" class="add-new-h2"><?php _e( 'Add New', 'wpforo' ); ?></a>
47
+ </h2>
48
+
49
+ <?php WPF()->notice->show() ?>
50
+
51
+ <!-- Forum Hierarchy -->
52
+ <?php if( ! $action ) : ?>
53
+ <?php if( WPF()->usergroup->can_manage_forum() ): ?>
54
+
55
+ <div class="wpf-info-bar" style="line-height: 1em; clear:both; padding: 5px 50px; box-sizing: border-box; font-size:15px; display:block; box-shadow:none; margin: 20px 0 10px 0; font-style: italic; background: #FFFFFF; width:100%; position: relative;">
56
+ <a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 16px; position: absolute; right: 15px; top: 15px;"><i class="far fa-question-circle"></i></a>
57
+ <ul style="list-style-type: disc; line-height:18px;">
58
+ <li style="list-style:none; margin-left:-17px; font-style:normal; font-weight:bold; padding-bottom: 5px;"><i class="fas fa-info-circle" aria-hidden="true"></i>&nbsp; <?php _e( 'Important Tips', 'wpforo' ); ?></li>
59
+ <li><?php _e( 'Please drag and drop forum panels to set parent-child hierarchy.', 'wpforo' ); ?></li>
60
+ <li><?php _e( 'If a category (blue panels) does not have forums (grey panels) it will not be displayed on front-end. Each category should contain at least one forum.', 'wpforo' ); ?></li>
61
+ <li><?php _e( 'Forums can be displayed with different layouts (Extended, Simplified, Q&A, Threaded), just edit the top (blue panels) category and set the layout you want. Child forums\' layout depends on the top category (blue panels) layout. They cannot have a different layout.', 'wpforo' ); ?></li>
62
+ </ul>
63
+ </div>
64
+ <br style="clear: both;"/>
65
+
66
+ <form id="forum-hierarchy" encType="multipart/form-data" method="POST">
67
+ <?php wp_nonce_field( 'wpforo-forums-hierarchy' ); ?>
68
+ <input type="hidden" name="wpfaction" value="forum_hierarchy_save">
69
+ <div id="post-body">
70
+ <ul id="menu-to-edit" class="menu">
71
+
72
+ <?php WPF()->forum->tree( 'drag_menu' ); ?>
73
+
74
+ </ul>
75
+ </div>
76
+ <br/>
77
+ <div class="major-publishing-actions">
78
+ <div class="publishing-action"><input id="save_menu_footer" class="button button-primary menu-save" name="save_menu" value="<?php _e( 'Save forums order and hierarchy', 'wpforo' ); ?>" onclick="get_forums_hierarchy()" type="button"></div>
79
+ </div>
80
+ </form>
81
+ <script>
82
+ var menus = false
83
+ navMenuL10n.saveAlert = null
84
+ window.onbeforeunload = function () {if (a.menusChanged) {return navMenuL10n.saveAlert}}
85
+ </script>
86
+ <?php endif; ?><!--checking edit forum permission-->
87
+ <?php endif; ?>
88
+ <!-- end Forum Hierarchy -->
89
+ <br style="clear: both;"/>
90
+ <!-- Forum Add || Edit -->
91
+ <?php if( in_array( $action, [ 'add', 'edit' ] ) ) : ?>
92
+ <?php if( WPF()->usergroup->can_manage_forum() ): ?>
93
+ <?php
94
+ $data = [];
95
+ $disabled_forumid = 0;
96
+ $selected_forumid = 0;
97
+ if( ! empty( $_GET['id'] ) ) {
98
+ $disabled_forumid = [ $_GET['id'] ];
99
+ if( $data = WPF()->forum->get_forum( [ 'forumid' => $_GET['id'] ] ) ) $selected_forumid = $data['parentid'];
100
+ }
101
+ if( ! empty( $_GET['parentid'] ) ) $selected_forumid = $_GET['parentid'];
102
+ $color = wpfval( $data, 'color' ) ? $data['color'] : wpforo_random_colors();
103
+ ?>
104
+ <style type="text/css">
105
+ #forum_layout .wpf-fl-box a:not(.wpf-lightbox) {
106
+ cursor: zoom-in;
107
+ display: inline-block;
108
+ }
109
+
110
+ .wpf-lightbox {
111
+ display: none;
112
+ cursor: zoom-out;
113
+ position: fixed;
114
+ z-index: 999;
115
+ width: 100%;
116
+ height: 100%;
117
+ text-align: center;
118
+ top: 0;
119
+ left: 0;
120
+ background: rgba(0, 0, 0, 0.8);
121
+ }
122
+
123
+ .wpf-lightbox img {
124
+ max-width: 90%;
125
+ max-height: 90%;
126
+ margin-top: 3%;
127
+ }
128
+
129
+ .wpf-lightbox:target {
130
+ outline: none;
131
+ display: block;
132
+ }
133
+
134
+ .wpf-fl-wrap {
135
+ display: flex;
136
+ flex-wrap: wrap;
137
+ flex-direction: row;
138
+ justify-content: space-evenly;
139
+ align-items: stretch;
140
+ }
141
+
142
+ .wpf-fl-box {
143
+ margin: 6px;
144
+ border: 1px solid #cccccc;
145
+ padding: 5px;
146
+ text-align: center;
147
+ }
148
+
149
+ .wpf-fl-box h4 {
150
+ cursor: pointer;
151
+ text-align: center;
152
+ margin: 1px;
153
+ font-size: 14px;
154
+ background: #bbbbbb;
155
+ color: #ffffff;
156
+ padding: 2px 1px 4px 1px;
157
+ }
158
+
159
+ .wpf-fl-box.wpf-fl-active {
160
+ border-color: #128403;
161
+ }
162
+
163
+ .wpf-fl-box.wpf-fl-active h4 {
164
+ background-color: #128403;
165
+ }
166
+ </style>
167
+ <script type="text/javascript">
168
+ jQuery(document).ready(function ($) {
169
+ $('.wpforo-color-field').wpColorPicker()
170
+
171
+ $('#forum_layout').on('click', '.wpf-fl-box:not(.wpf-fl-active) h4', function () {
172
+ if ($('#use_us_cat').is(':checked')) {
173
+ var wrap = $(this).parents('.wpf-fl-box')
174
+ if (wrap) {
175
+ var layout = wrap.data('layout')
176
+ if (layout) {
177
+ $('#layout option[value="' + layout + '"]').attr('selected', true)
178
+ $('.wpf-fl-box').removeClass('wpf-fl-active')
179
+ wrap.addClass('wpf-fl-active')
180
+ }
181
+ }
182
+ }
183
+ })
184
+ $('#layout').on('change', function () {
185
+ $('.wpf-fl-box').removeClass('wpf-fl-active')
186
+ $('#wpfl-' + this.value).addClass('wpf-fl-active')
187
+ })
188
+ $('#use_us_cat').on('change', function () {
189
+ if( !$(this).is(':checked') ) {
190
+ $( '#wpf_forum_cover_field' ).hide();
191
+ <?php if( wpfkey( $data, 'is_cat' ) && ! wpfval( $data, 'is_cat' ) ): ?>
192
+ $('.wpf-fl-box').removeClass('wpf-fl-active')
193
+ var wpf_layout = $('#wpf-current-layout').val()
194
+ $('#wpfl-' + wpf_layout).addClass('wpf-fl-active')
195
+ <?php else: ?>
196
+ $('.wpf-fl-box').removeClass('wpf-fl-active')
197
+ $('#forum_layout').hide()
198
+ <?php endif; ?>
199
+ } else {
200
+ $( '#wpf_forum_cover_field' ).show();
201
+ $('.wpf-fl-box').removeClass('wpf-fl-active')
202
+ var wpf_layout = $('#layout').find('option:selected').val()
203
+ $('#wpfl-' + wpf_layout).addClass('wpf-fl-active')
204
+ $('#forum_layout').show()
205
+ }
206
+ })
207
+ })
208
+ </script>
209
+ <div id="poststuff">
210
+ <form name="forum" method="POST">
211
+ <?php if( $action === 'add' ) : ?>
212
+ <?php wp_nonce_field( 'wpforo-forum-add' ); ?>
213
+ <input type="hidden" name="wpfaction" value="forum_add">
214
+ <?php elseif( $action === 'edit' ) : ?>
215
+ <?php wp_nonce_field( 'wpforo-forum-edit' ); ?>
216
+ <input type="hidden" name="wpfaction" value="forum_edit">
217
+ <input type="hidden" name="forum[forumid]" value="<?php echo (int) wpfval( $_GET, 'id' ) ?>">
218
+ <?php endif ?>
219
+
220
+ <div id="post-body" class="metabox-holder columns-2">
221
+ <div id="post-body-content">
222
+ <input type="hidden" name="wpforo_submit" value="1"/>
223
+ <input type="hidden" name="forum[order]" value="<?php echo esc_attr( isset( $data['order'] ) ? $data['order'] : '' ) ?>"/>
224
+ <div class="form-wrap">
225
+ <div class="form-field form-required" style="margin-bottom:0px; padding-bottom:0px;">
226
+ <div id="titlediv">
227
+ <div id="titlewrap">
228
+ <input id="title" name="forum[title]" type="text" value="<?php echo esc_attr( isset( $data['title'] ) ? $data['title'] : '' ) ?>" size="40" autocomplete="off" required="TRUE" placeholder="<?php _e( 'Enter forum title here', 'wpforo' ); ?>"/>
229
+ </div>
230
+ </div>
231
+ <p>&nbsp;</p>
232
+ <div class="form-field">
233
+ <textarea placeholder="<?php _e( 'Enter description here . . .', 'wpforo' ); ?>" name="forum[description]" rows="2" cols="40" style="padding:10px;"><?php echo esc_textarea( isset( $data['description'] ) ? $data['description'] : '' ) ?></textarea>
234
+ <p><?php _e( 'This is a forum description. This content will be displayed under forum title on the forum list.', 'wpforo' ); ?></p>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+
240
+ <div id="postbox-container-1" class="postbox-container">
241
+ <div id="side-sortables" class="meta-box-sortables ui-sortable">
242
+ <div id="forum_cat" class="postbox">
243
+ <h3 class="wpf-box-header"><span><?php _e( 'Forum Options', 'wpforo' ); ?> &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-options" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span></h3>
244
+ <div class="inside">
245
+ <div class="form-field">
246
+ <p><strong><?php _e( 'Parent Forum', 'wpforo' ); ?></strong></p>
247
+ <p>
248
+ <select id="parent" name="forum[parentid]" class="postform" <?php echo( isset( $data['is_cat'] ) && $data['is_cat'] == 1 ? 'disabled' : '' ) ?>>
249
+ <option value="0"><?php _e( 'No parent', 'wpforo' ); ?></option>
250
+ <?php WPF()->forum->tree( 'select_box', true, $selected_forumid, false, $disabled_forumid ); ?>
251
+ </select>
252
+ </p>
253
+ <p class="form-field">
254
+ <label for="use_us_cat"><?php _e( 'Use as Category', 'wpforo' ); ?> &nbsp;<input id="use_us_cat" onclick="document.getElementById('parent').disabled = this.checked; document.getElementById('layout').disabled = !this.checked;" type="checkbox" name="forum[is_cat]" value="1" <?php echo( isset( $data['is_cat'] ) && $data['is_cat'] == 1 ? 'checked' : '' ) ?>/> </label>
255
+ </p>
256
+ <p><strong><?php _e( 'Category Layout', 'wpforo' ); ?></strong></p>
257
+ <p>
258
+ <?php $layouts = WPF()->tpl->find_layouts(); ?>
259
+ <?php if( ! empty( $layouts ) ): ?>
260
+ <select id="layout" name="forum[layout]" class="postform" <?php $data['layout'] = ( isset( $data['layout'] ) ? $data['layout'] : 1 );
261
+ echo( isset( $data['is_cat'] ) && $data['is_cat'] == 1 ? '' : 'disabled="TRUE"' ); ?> >
262
+ <?php WPF()->tpl->show_layout_selectbox( $data['layout'] ); ?>
263
+ </select>
264
+ <?php else: ?>
265
+ <p><?php _e( 'No layout found.', 'wpforo' ); ?></p>
266
+ <?php endif; ?>
267
+ </p>
268
+ </div>
269
+ </div>
270
+ </div>
271
+
272
+ <div id="submitdiv" class="postbox">
273
+ <h3 class="wpf-box-header"><span><?php _e( 'Publish', 'wpforo' ); ?></span></h3>
274
+ <div class="inside">
275
+ <div id="major-publishing-actions" style="text-align:right;">
276
+ <?php if( $action === 'edit' ) : ?>
277
+ <a class="wpf-delete button" href="?page=<?php echo wpforo_prefix_slug( 'forums' ) ?>&id=<?php echo intval( wpfval($data, 'forumid') ) ?>&action=del" onclick="if (!confirm('<?php _e( 'Are you sure you want to delete this forum?', 'wpforo' ); ?>')) { return false; }"><?php _e( 'Delete', 'wpforo' ); ?></a> &nbsp;
278
+ <a class="preview button" href="<?php echo wpforo_home_url( ( isset( $data['slug'] ) ? $data['slug'] : '' ) ) ?>" target="wp-preview" id="post-preview" style="display:inline-block;float:none;"><?php _e( 'View', 'wpforo' ); ?></a> &nbsp;
279
+ <input type="submit" name="forum[save_edit]" class="button button-primary forum_submit" style="display:inline-block;float:none;" value="<?php _e( 'Update', 'wpforo' ); ?>">
280
+ <?php else: ?>
281
+ <input type="submit" name="forum[save_edit]" class="button button-primary forum_submit" style="display:inline-block;float:none;" value="<?php _e( 'Publish', 'wpforo' ); ?>">
282
+ <?php endif; ?>
283
+ <div class="clear"></div>
284
+ </div>
285
+ </div>
286
+ </div>
287
+
288
+
289
+ <div id="forum_permissions" class="postbox">
290
+ <h3 class="wpf-box-header"><span><?php _e( 'Forum Permissions', 'wpforo' ); ?> &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-permissions" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span></h3>
291
+ <div class="inside">
292
+ <table>
293
+ <?php WPF()->forum->permissions(); ?>
294
+ </table>
295
+ </div>
296
+ </div>
297
+
298
+ <div id="forum_color" class="postbox">
299
+ <h3 class="wpf-box-header"><span><?php _e( 'Additional Options', 'wpforo' ); ?> &nbsp;</span></h3>
300
+ <div class="inside">
301
+ <div class="wpf-forum-additional-options">
302
+ <div style="float: left; padding: 2px 10px 0 0;"><?php _e( 'Forum Color', 'wpforo' ) ?></div>
303
+ <div class="wpforo-style-field">
304
+ <input class="wpforo-color-field" name="forum[color]" type="text" value="<?php wpfo( strtoupper( $color ) ); ?>" title="<?php wpfo( strtoupper( $color ) ); ?>"/>
305
+ </div>
306
+ <div style="clear: both"></div>
307
+ </div>
308
+ </div>
309
+ </div>
310
+
311
+ <?php do_action( 'wpforo_dashboard_forum_form_side', $data ); ?>
312
+
313
+ </div>
314
+ </div>
315
+ <div id="postbox-container-2" class="postbox-container">
316
+ <div id="normal-sortables" class="meta-box-sortables ui-sortable">
317
+
318
+ <div id="forum_layout" class="postbox" <?php if( ! wpfkey( $data, 'is_cat' ) ) echo 'style="display: none"'; ?>>
319
+ <h3 class="wpf-box-header">
320
+ <span>
321
+ <?php if( wpfkey( $data, 'is_cat' ) ): ?>
322
+ <?php if( wpfval( $data, 'is_cat' ) ): ?>
323
+ <?php _e( 'Current layout and cover image of this category', 'wpforo' ); ?>
324
+ <?php else: ?>
325
+ <?php _e( 'Current layout of this forum (inherited from parent category)', 'wpforo' ); ?>
326
+ <?php endif; ?>
327
+ <?php else: ?>
328
+ <?php _e( 'Category layout and cover image', 'wpforo' ); ?>
329
+ <?php endif; ?>
330
+ &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-layouts/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a>
331
+ </span>
332
+ </h3>
333
+ <div class="inside" style="padding: 5px 7px;">
334
+ <div class="wpf-fl-wrap">
335
+ <input type="hidden" id="wpf-current-layout" name="wpf-current-layout" value="<?php if( wpfval( $data, 'layout' ) ) echo intval( $data['layout'] ) ?>">
336
+ <?php $layouts = WPF()->tpl->find_layouts(); $theme = WPF()->tpl->theme;
337
+ if( ! empty( $layouts ) ): foreach( $layouts as $layout ) : ?>
338
+ <div id="wpfl-<?php echo intval( $layout['id'] ) ?>" data-layout="<?php echo intval( $layout['id'] ) ?>" class="wpf-fl-box <?php if( wpfkey( $data, 'is_cat' ) && $layout['id'] == $data['layout'] ) echo 'wpf-fl-active' ?>">
339
+ <a href="#img1<?php echo intval( $layout['id'] ); ?>"><img src="<?php echo WPFORO_URL ?>/themes/<?php echo esc_attr($theme) ?>/layouts/<?php echo intval( $layout['id'] ); ?>/view-forums.png" style="height: 100px;"/></a>
340
+ <a href="#_" class="wpf-lightbox" id="img1<?php echo intval( $layout['id'] ); ?>"><img src="<?php echo WPFORO_URL ?>/themes/<?php echo esc_attr($theme) ?>/layouts/<?php echo intval( $layout['id'] ); ?>/view-forums.png"/></a>
341
+ <a href="#img2<?php echo intval( $layout['id'] ); ?>"><img src="<?php echo WPFORO_URL ?>/themes/<?php echo esc_attr($theme) ?>/layouts/<?php echo intval( $layout['id'] ); ?>/view-posts.png" style="height: 100px;"/></a>
342
+ <a href="#_" class="wpf-lightbox" id="img2<?php echo intval( $layout['id'] ); ?>"><img src="<?php echo WPFORO_URL ?>/themes/<?php echo esc_attr($theme) ?>/layouts/<?php echo intval( $layout['id'] ); ?>/view-posts.png"/></a>
343
+ <h4><?php echo esc_html( $layout['name'] ) ?><!-- <input type="radio" name="forum[layout]" value="<?php echo esc_attr( trim( $layout['id'] ) ) ?>" <?php echo( $layout['id'] == $data['layout'] ? ' checked="checked" ' : '' ); ?> /> --></h4>
344
+ </div>
345
+ <?php endforeach; endif; ?>
346
+ </div>
347
+ <p style="font-style: italic; padding: 0 10px 5px; margin: 10px 0;"><i class="fas fa-info-circle" style="color: #777777; font-size: 14px; padding-right: 3px;"></i> <?php _e(
348
+ 'Forums can be displayed with different layouts (Extended, Simplified, Q&A, Threaded). Use the "Category Layout" dropdown located on the top right section to set the layout you want. This option is only available for Categories (top parent forums). Other forums and sub-forums inherit it. They cannot have a different layout, therefore the layout dropdown option becomes disabled if this forum is not a Category.',
349
+ 'wpforo'
350
+ ); ?></p>
351
+
352
+
353
+ <?php $show_cover_field = ! (int) wpfval($data, 'forumid') || (int) wpfval($data, 'is_cat'); ?>
354
+ <div id="wpf_forum_cover_field" style="padding: 10px; <?php echo ( $show_cover_field ? 'display: block' : 'display: none' ) ?>">
355
+ <h3 style="font-size: 15px;"><?php _e('Category Cover Image', 'wpforo') ?></h3>
356
+ <div id="wpf-forum-cover" style="background-image: url('<?php echo esc_url_raw( wpfval( $data, 'cover_url' ) ) ?>')">
357
+ <?php
358
+ if( wpfval( $data, 'cover_url' ) ) {
359
+ echo '<a href="#" id="wpf-forum-cover-upload" class="wpf-cat-cover-upl button button-secondary" style="display:none">' . __('Upload image', 'wpforo') . '</a>
360
+ <a href="#" id="wpf-forum-cover-remove" class="wpf-cat-cover-rmv button button-secondary">' . __('Remove image', 'wpforo') . '</a>
361
+ <input id="wpf-forum-cover-filed" type="hidden" name="forum[cover]" value="' . $data['cover'] . '">';
362
+
363
+ } else {
364
+ echo '<a href="#" id="wpf-forum-cover-upload" class="wpf-cat-cover-upl button button-secondary">' . __('Upload image', 'wpforo') . '</a>
365
+ <a href="#" id="wpf-forum-cover-remove" class="wpf-cat-cover-rmv button button-secondary" style="display:none">' . __('Remove image', 'wpforo') . '</a>
366
+ <input id="wpf-forum-cover-filed" type="hidden" name="forum[cover]" value="">';
367
+
368
+ }
369
+ ?>
370
+ </div>
371
+ </div>
372
+
373
+ <script>
374
+ jQuery(function($){
375
+ $('body').on( 'click', '.wpf-cat-cover-upl', function(e){
376
+ e.preventDefault();
377
+ var button = $(this),
378
+ custom_uploader = wp.media({
379
+ title: '<?php echo __('Insert image', 'wpforo') ?>',
380
+ library : { type : 'image' },
381
+ button: { text: '<?php echo __('Use this image', 'wpforo') ?>'},
382
+ multiple: false
383
+ }).on('select', function() { // it also has "open" and "close" events
384
+ var attachment = custom_uploader.state().get('selection').first().toJSON();
385
+ $('#wpf-forum-cover').css("background-image", "url(" + attachment.url + ")");
386
+ $('#wpf-forum-cover-filed').val(attachment.id);
387
+ $('#wpf-forum-cover-upload').hide();
388
+ $('#wpf-forum-cover-remove').show();
389
+ }).open();
390
+ });
391
+ $('body').on('click', '.wpf-cat-cover-rmv', function(e){
392
+ e.preventDefault();
393
+ var button = $(this);
394
+ $('#wpf-forum-cover-filed').val('');
395
+ $('#wpf-forum-cover-remove').hide();
396
+ $('#wpf-forum-cover-upload').show();
397
+ $('#wpf-forum-cover').css("background-image", "");
398
+ });
399
+ });
400
+ </script>
401
+
402
+ </div>
403
+ </div>
404
+
405
+ <div id="forum_slug" class="postbox">
406
+ <h3 class="wpf-box-header"><span><?php _e( 'Forum Slug', 'wpforo' ); ?> &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-slug" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span></h3>
407
+ <div class="inside">
408
+ <input name="forum[slug]" type="text" value="<?php echo esc_attr( isset( $data['slug'] ) ? $data['slug'] : '' ) ?>" size="40"/>
409
+ <p><?php _e( 'The "slug" is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.', 'wpforo' ); ?> </p><br/>
410
+ </div>
411
+ </div>
412
+
413
+ <div id="forum_icon" class="postbox">
414
+ <h3 class="wpf-box-header"><span><?php _e( 'Forum Icon', 'wpforo' ); ?> &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-icon" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span></h3>
415
+ <div class="inside" style="padding-top:10px;">
416
+ <div class="form-field">
417
+ <label for="tag-icon" style="display:block; padding-bottom:5px;"><?php _e( 'Font-awesome Icon', 'wpforo' ); ?>:</label>
418
+ <div><input name="forum[icon]" value="<?php echo ( isset( $data['icon'] ) && $data['icon'] ) ? esc_attr( $data['icon'] ) : 'fas fa-comments'; ?>" type="text"></div>
419
+ <p style="margin-bottom:0; margin-top:5px;"><?php _e( 'You can find all icons', 'wpforo' ); ?> <a href="http://fontawesome.io/icons/" target="_blank"><?php _e( 'here', 'wpforo' ); ?>.</a> <?php _e( 'Make sure you insert a class of font-awesome icon, it should start with fa- prefix like &quot;fas fa-comments&quot;.', '' ) ?></p>
420
+ </div>
421
+ </div>
422
+ </div>
423
+
424
+ <div id="forum_meta" class="postbox">
425
+ <h3 class="wpf-box-header"><span><?php _e( 'Forum SEO Description', 'wpforo' ); ?></span></h3>
426
+ <div class="inside" style="padding-top:10px;">
427
+ <div class="form-field">
428
+ <label for="tag-description" style="display:block; padding-bottom:5px;"><?php _e( 'Meta Description', 'wpforo' ); ?>:</label>
429
+ <textarea name="forum[meta_desc]" rows="3" cols="40"><?php echo esc_html( isset( $data['meta_desc'] ) ? $data['meta_desc'] : '' ) ?></textarea>
430
+ </div>
431
+ </div>
432
+ </div>
433
+
434
+ <?php do_action( 'wpforo_dashboard_forum_form_main', $data ); ?>
435
+
436
+ </div>
437
+ <div id="advanced-sortables" class="meta-box-sortables ui-sortable"></div>
438
+ </div>
439
+
440
+ </div>
441
+ </form>
442
+ </div>
443
+ <?php endif; ?><!-- chekcing creat forum permission-->
444
+ <?php endif; ?>
445
+ <!-- end Forum Add || Edit -->
446
+
447
+ <!-- Forum Delete -->
448
+ <?php if( $action === 'del' ) : ?>
449
+ <form method="POST">
450
+ <?php wp_nonce_field( 'wpforo-forum-delete' ); ?>
451
+ <input type="hidden" name="wpfaction" value="forum_delete">
452
+ <div class="form-wrap">
453
+ <div class="form-field form-required">
454
+ <div class="form-field wpf-info-bar" style="padding:25px 20px 15px 20px; margin-top:20px;">
455
+ <table class="wpforo_settings_table">
456
+ <tr>
457
+ <td style="width:50%;">
458
+ <label for="delete_forum" class="menu_delete" style="color: red; font-size:13px; line-height:18px;"><?php _e( 'This action will also delete all sub-forums, topics and replies.', 'wpforo' ); ?></label>
459
+ </td>
460
+ <td>
461
+ <input id="delete_forum" type="radio" name="forum[delete]" value="1" checked="" onchange="mode_changer(false);">
462
+ </td>
463
+ </tr>
464
+ <tr>
465
+ <td>
466
+ <label for="marge" style="font-size:13px; line-height:18px;"><?php _e( 'If you want to delete this forum and keep its sub-forums, topics and replies, please select a new target forum in dropdown below', 'wpforo' ); ?></label>
467
+ </td>
468
+ <td><input id="marge" type="radio" name="forum[delete]" value="0" onchange="mode_changer(true);"></td>
469
+ </tr>
470
+ <tr>
471
+ <td colspan="2">
472
+ <select id="forum_select" name="forum[mergeid]" class="postform" disabled="">
473
+ <?php WPF()->forum->tree( 'select_box', false ); ?>
474
+ </select>
475
+ <p><?php _e( 'All sub-forums, topics and replies will be attached to selected forum. Layout will be inherited from this forum.', 'wpforo' ); ?></p>
476
+ </td>
477
+ </tr>
478
+ <tr>
479
+ <td colspan="2">
480
+ <input id="forum_submit" type="submit" name="forum[submit]" class="button button-primary" value="Delete">
481
+ </td>
482
+ </tr>
483
+ </table>
484
+ </div>
485
+ </div>
486
+ </div>
487
+ </form>
488
+ <?php endif; ?>
489
+ <!-- end Forum Delete -->
490
+
491
+ </div><!-- wpwrap -->
{wpf-admin/tools-tabs → admin/pages}/index.html RENAMED
File without changes
admin/pages/member.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! WPF()->usergroup->can( 'vm' ) ) exit;
5
+ ?>
6
+
7
+ <div id="wpf-admin-wrap" class="wrap">
8
+ <?php wpforo_screen_option() ?>
9
+ <div id="icon-users" class="icon32"><br></div>
10
+ <h2 style="padding:30px 0px 0px 0px;line-height: 20px; margin-bottom:15px;"><?php _e( 'Members', 'wpforo' ); ?></h2>
11
+ <?php WPF()->notice->show() ?>
12
+
13
+ <form method="get">
14
+ <input type="hidden" name="page" value="<?php echo wpforo_prefix_slug( 'members' ) ?>">
15
+ <?php WPF()->member->list_table->groups_dropdown() ?>
16
+ <?php WPF()->member->list_table->status_dropdown() ?>
17
+ <input type="submit" value="<?php _e( 'Filter', 'wpforo' ) ?>" class="button button-large">
18
+
19
+ <?php WPF()->member->list_table->search_box( 'Search Members', 'wpf-member-search' ) ?>
20
+ </form>
21
+ <br>
22
+ <hr>
23
+ <?php
24
+ $all_count = WPF()->member->get_count();
25
+ $active_count = WPF()->member->get_count( [ 'p.status' => 'active' ] );
26
+ $inactive_count = WPF()->member->get_count( [ 'p.status' => 'inactive' ] );
27
+ $banned_count = WPF()->member->get_count( [ 'p.status' => 'banned' ] );
28
+ $all_href = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'members' ) );
29
+ $active_href = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'members' ) . '&filter_by_status=active' );
30
+ $inactive_href = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'members' ) . '&filter_by_status=inactive' );
31
+ $banned_href = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'members' ) . '&filter_by_status=banned' );
32
+ $all_attr = '';
33
+ $active_attr = '';
34
+ $inactive_attr = '';
35
+ $banned_attr = '';
36
+ $filter_by_status = WPF()->member->list_table->get_filter_by_status_var();
37
+ if( $filter_by_status === 'active' ) {
38
+ $active_attr = ' class="current"';
39
+ } elseif( $filter_by_status === 'inactive' ) {
40
+ $inactive_attr = ' class="current"';
41
+ } elseif( $filter_by_status === 'banned' ) {
42
+ $banned_attr = ' class="current"';
43
+ } else {
44
+ $all_attr = ' class="current"';
45
+ }
46
+
47
+ ?>
48
+ <ul class="subsubsub">
49
+ <li>
50
+ <a href="<?php echo $all_href ?>" <?php echo $all_attr ?>>
51
+ <?php _e( 'All', 'wpforo' ); ?>
52
+ <span class="count">(<?php echo $all_count; ?>)</span>
53
+ </a> |
54
+ </li>
55
+ <li>
56
+ <a href="<?php echo $active_href ?>" <?php echo $active_attr ?>>
57
+ <?php _e( 'Active', 'wpforo' ); ?>
58
+ <span class="count">(<?php echo $active_count; ?>)</span>
59
+ </a> |
60
+ </li>
61
+ <li>
62
+ <a href="<?php echo $inactive_href ?>" <?php echo $inactive_attr ?>>
63
+ <?php _e( 'Inactive', 'wpforo' ); ?>
64
+ <span class="count">(<?php echo $inactive_count; ?>)</span>
65
+ </a> |
66
+ </li>
67
+ <li>
68
+ <a href="<?php echo $banned_href ?>" <?php echo $banned_attr ?>>
69
+ <?php _e( 'Banned', 'wpforo' ); ?>
70
+ <span class="count">(<?php echo $banned_count; ?>)</span>
71
+ </a>
72
+ </li>
73
+ </ul>
74
+ <!-- Forms are NOT created automatically, so you need to wrap the table in one to use features like bulk actions -->
75
+ <form id="wpf-dashboard-members-page" method="GET">
76
+ <!-- For plugins, we also need to ensure that the form posts back to our current page -->
77
+ <input type="hidden" name="page" value="<?php echo wpforo_prefix_slug( 'members' ) ?>">
78
+ <input type="hidden" name="wpfaction" value="bulk_members">
79
+
80
+ <!-- Now we can render the completed list table -->
81
+ <?php WPF()->member->list_table->display() ?>
82
+ </form>
83
+ </div>
admin/pages/moderation.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! WPF()->usergroup->can( 'aum' ) ) exit;
5
+ ?>
6
+
7
+ <div id="wpf-admin-wrap" class="wrap" style="margin-top: 0">
8
+ <?php wpforo_screen_option() ?>
9
+ <div id="icon-users" class="icon32"><br></div>
10
+ <h2 style="padding: 30px 0 0 0; line-height: 20px; margin-bottom: 15px;"><?php _e( 'Topic and Post Moderation', 'wpforo' ); ?></h2>
11
+ <?php WPF()->notice->show() ?>
12
+
13
+ <form method="get">
14
+ <input type="hidden" name="page" value="<?php echo wpforo_prefix_slug( 'moderations' ) ?>">
15
+ <?php WPF()->moderation->list_table->users_dropdown() ?>
16
+ <?php WPF()->moderation->list_table->status_dropdown() ?>
17
+ <?php WPF()->moderation->list_table->type_dropdown() ?>
18
+ <input type="submit" value="<?php _e( 'Filter', 'wpforo' ) ?>" class="button button-large">
19
+
20
+ <?php WPF()->moderation->list_table->search_box( 'Search Posts', 'wpf-post-search' ) ?>
21
+ </form>
22
+ <br>
23
+ <hr>
24
+ <?php
25
+ $unapproveds = WPF()->post->get_count( [ 'status' => 1 ] );
26
+ $approveds = WPF()->post->get_count( [ 'status' => 0 ] );
27
+ $uhref = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'moderations' ) . '&filter_by_status=1' );
28
+ $ahref = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'moderations' ) . '&filter_by_status=0' );
29
+ if( WPF()->moderation->list_table->get_filter_by_status_var() ) {
30
+ $uattr = ' class="current"';
31
+ $aattr = '';
32
+ } else {
33
+ $uattr = '';
34
+ $aattr = ' class="current"';
35
+ }
36
+
37
+ ?>
38
+ <ul class="subsubsub">
39
+ <li>
40
+ <a href="<?php echo $uhref ?>" <?php echo $uattr ?>>
41
+ <?php _e( 'Unapproved', 'wpforo' ); ?>
42
+ <span class="count">(<?php echo $unapproveds; ?>)</span>
43
+ </a> |
44
+ </li>
45
+ <li>
46
+ <a href="<?php echo $ahref ?>" <?php echo $aattr ?>>
47
+ <?php _e( 'Published', 'wpforo' ); ?>
48
+ <span class="count">(<?php echo $approveds; ?>)</span>
49
+ </a>
50
+ </li>
51
+ </ul>
52
+ <!-- Forms are NOT created automatically, so you need to wrap the table in one to use features like bulk actions -->
53
+ <form id="wpf-dashboard-moderation-page" method="GET">
54
+ <!-- For plugins, we also need to ensure that the form posts back to our current page -->
55
+ <input type="hidden" name="page" value="<?php echo wpforo_prefix_slug( 'moderations' ) ?>">
56
+ <input type="hidden" name="wpfaction" value="bulk_moderation">
57
+
58
+ <!-- Now we can render the completed list table -->
59
+ <?php WPF()->moderation->list_table->display() ?>
60
+ </form>
61
+ </div>
admin/pages/overview.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ ?>
5
+
6
+ <div id="wpf-admin-wrap" class="wrap">
7
+ <h1 style="margin: 0; padding: 0; line-height: 10px;">&nbsp</h1>
8
+ <?php WPF()->notice->show() ?>
9
+
10
+ <div id="dashboard-widgets-wrap" style="padding-top:10px;">
11
+ <div class="metabox-holder" id="dashboard-widgets">
12
+
13
+ <div class="postbox-container" id="postbox-container-0" style="width:100%;">
14
+ <div class="wpf-box-wrap" style="min-height:60px;">
15
+
16
+ <div class="postbox" id="wpforo_dashboard_widget_0" style="margin: 10px; padding: 20px 15px 10px 15px;">
17
+ <div class="inside">
18
+ <div class="main" style="padding:5px 15px 15px 15px;">
19
+ <div style="float:left; vertical-align:top; width:calc(100% - 150px);;">
20
+ <p style="font-size:30px; margin:0 0 10px; font-family:Constantia, 'Lucida Bright', 'DejaVu Serif', Georgia, serif">
21
+ <?php _e( 'Welcome to wpForo', 'wpforo' ); echo ' ' . esc_html( WPFORO_VERSION ) ?>
22
+ </p>
23
+ <p style="margin:0; font-size:14px; font-family:'Lucida Bright', 'DejaVu Serif', Georgia, serif">
24
+ <?php _e( 'Thank you for using wpForo! wpForo is a professional bulletin board for WorPress, and the only forum software which comes with Multi-layout template system.
25
+ The "Extended", "Simplified", "Q&A" and "Threaded" layouts fit almost all type of discussions needs. You can use wpForo for small and extremely large communities. If you found some issue or bug please open a support topic in wpForo Support forum at wpForo.com. If you liked wpForo please leave some good review for this plugin. We really need your good reviews.
26
+ If you didn\'t like wpForo please leave a list of issues and requirements you\'d like us to fix and add in our support forum. We\'re here to help you and improve wpForo as much as possible.',
27
+ 'wpforo'
28
+ ); ?>
29
+ </p>
30
+ <h3 style="font-size: 18px; margin-top: 20px;"><?php _e("What's New in wpForo 2", 'wpforo') ?></h3>
31
+ <ul class="wpf-wn-list">
32
+ <li><span class="wpf-wn-label"><?php _e("Multi-board system:", 'wpforo') ?></span> <?php _e("Allows to create multiple separate forum boards in the same WordPress website. You can create fully separate forums in different pages,", 'wpforo') ?></li>
33
+ <li><span class="wpf-wn-label"><?php _e("Multi-language forums:", 'wpforo') ?></span> <?php _e("Using the multi-board system you can create multiple separate forums for each language of your website. The forums will have different pages, categories and threads,", 'wpforo') ?></li>
34
+ <li><span class="wpf-wn-label"><?php _e("New Forum Theme:", 'wpforo') ?></span> <?php _e("The new version comes with completely redesigned forum layouts and style. It brings modern and clean look and feel for all 4 forum layouts (Extended, Simplified, Q&A and Threaded),", 'wpforo') ?></li>
35
+ <li><span class="wpf-wn-label"><?php _e("New Member Profile System:", 'wpforo') ?></span> <?php _e("wpForo 2 comes with more powerful member profile system with awesome design, so in most cases you won't need to use other profile builder plugins,", 'wpforo') ?></li>
36
+ <li><span class="wpf-wn-label"><?php _e("Topic Overview:", 'wpforo') ?></span> <?php _e("You can see a small panel on the top of all topics, which provides lots of quick information about the topic. Different statistic information and a shorthand tree of the whole topic with discussion threads and nested replies in one place. This makes it very quick find the posts you need in the topic,", 'wpforo') ?></li>
37
+ <li><span class="wpf-wn-label"><?php _e("And lots of more:", 'wpforo') ?></span> <a href="https://wpforo.com/community/wpforo-announcements/wpforo-2-0-0-is-released/" target="_blank"><?php _e("wpForo 2 release summary", 'wpforo') ?> &raquo;</a></li>
38
+ </ul>
39
+ </div>
40
+ <div style="float:right; vertical-align:top; padding-right:0; width:150px; text-align:right; padding-top:20px;">
41
+ <img class="wpforo-dashboard-logo" src="<?php echo WPFORO_URL ?>/assets/images/wpforo-logo.png" alt="wpforo logo">
42
+ <p style="font-size:11px; color:#B1B1B1; font-style:italic; text-align:right; line-height:14px; padding-top:15px; margin:0;">
43
+ <?php _e( 'Thank you!<br> Sincerely yours,<br> gVectors Team', 'wpforo' ); ?>&nbsp;
44
+ </p>
45
+ </div>
46
+ <div style="clear:both;"></div>
47
+ </div>
48
+ </div>
49
+ </div><!-- widget / postbox -->
50
+
51
+ </div>
52
+ </div>
53
+
54
+ <?php if( current_user_can( 'administrator' ) || current_user_can( 'editor' ) || current_user_can( 'author' ) ): ?>
55
+ <div class="postbox-container" style="width: 100%;">
56
+ <div class="wpf-box-wrap">
57
+
58
+ <div class="postbox wpf-dash-box" id="wpforo_dashboard_widget_server">
59
+ <h2 class="wpf-box-header"><span><?php _e( 'Server Information', 'wpforo' ); ?></span></h2>
60
+ <div class="inside">
61
+ <div class="main">
62
+ <table style="width:98%; margin:0 auto; text-align:left;">
63
+ <tr class="wpf-dw-tr">
64
+ <td class="wpf-dw-td">USER AGENT</td>
65
+ <td class="wpf-dw-td-value"><?php echo esc_html( $_SERVER['HTTP_USER_AGENT'] ) ?></td>
66
+ </tr>
67
+ <tr class="wpf-dw-tr">
68
+ <td class="wpf-dw-td">Web Server</td>
69
+ <td class="wpf-dw-td-value"><?php echo esc_html( $_SERVER['SERVER_SOFTWARE'] ) ?></td>
70
+ </tr>
71
+ <tr class="wpf-dw-tr">
72
+ <td class="wpf-dw-td">PHP Version</td>
73
+ <td class="wpf-dw-td-value"><?php echo phpversion(); ?></td>
74
+ </tr>
75
+ <tr class="wpf-dw-tr">
76
+ <td class="wpf-dw-td">MySQL Version</td>
77
+ <td class="wpf-dw-td-value"><?php echo WPF()->db->get_var( "SELECT VERSION()" ); ?></td>
78
+ </tr>
79
+ <tr class="wpf-dw-tr">
80
+ <td class="wpf-dw-td">PHP Max Post Size</td>
81
+ <td class="wpf-dw-td-value"><?php echo ini_get( 'post_max_size' ); ?></td>
82
+ </tr>
83
+ <tr class="wpf-dw-tr">
84
+ <td class="wpf-dw-td">PHP Max Upload Size</td>
85
+ <td class="wpf-dw-td-value"><?php echo ini_get( 'upload_max_filesize' ); ?></td>
86
+ </tr>
87
+ <tr class="wpf-dw-tr">
88
+ <td class="wpf-dw-td">PHP Memory Limit</td>
89
+ <td class="wpf-dw-td-value"><?php echo ini_get( 'memory_limit' ); ?></td>
90
+ </tr>
91
+ <tr class="wpf-dw-tr">
92
+ <td class="wpf-dw-td">PHP DateTime Class</td>
93
+ <td class="wpf-dw-td-value" style="line-height: 18px!important;">
94
+ <?php echo ( class_exists( 'DateTime' ) && class_exists( 'DateTimeZone' ) && method_exists( 'DateTime', 'setTimestamp' ) ) ? '<span class="wpf-green">' . __( 'Available', 'wpforo' ) . '</span>' : '<span class="wpf-red">' . __(
95
+ 'The setTimestamp() method of PHP DateTime class is not available. Please make sure you use PHP 5.4 and higher version on your hosting service.',
96
+ 'wpforo'
97
+ ) . '</span> | <a href="http://php.net/manual/en/datetime.settimestamp.php" target="_blank">more info&raquo;</a>'; ?> </td>
98
+ </tr>
99
+ <?php do_action( 'wpforo_dashboard_widget_server' ) ?>
100
+ </table>
101
+ </div>
102
+ </div>
103
+ </div><!-- widget / postbox -->
104
+
105
+ <?php if( wpforo_current_user_is( 'admin' ) ) : ?>
106
+ <div class="postbox wpf-dash-box" id="wpforo_dashboard_widget_0">
107
+ <h2 class="wpf-box-header"><span><?php _e( 'General Maintenance', 'wpforo' ); ?></span></h2>
108
+ <p class="wpf-info" style="padding:10px;"><?php _e( "This process may take a few seconds or dozens of minutes, please be patient and don't close this page. If you got 500 Server Error please don't worry, the data updating process is still working in MySQL server.", 'wpforo' ); ?></p>
109
+ <div class="inside">
110
+ <div class="main">
111
+
112
+ <div style="width:100%; padding:7px;">
113
+ <?php
114
+ $synch_user_profiles = wp_nonce_url( admin_url( 'admin.php?page=wpforo-overview&wpfaction=synch_user_profiles' ), 'wpforo_synch_user_profiles' );
115
+ $reset_users_stat_url = wp_nonce_url( admin_url( 'admin.php?page=wpforo-overview&wpfaction=reset_users_stats' ), 'wpforo_reset_users_stat' );
116
+ $reset_user_cache = wp_nonce_url( admin_url( 'admin.php?page=wpforo-overview&wpfaction=reset_user_cache' ), 'wpforo_reset_user_cache' );
117
+ ?>
118
+ <a href="<?php echo esc_url( $reset_users_stat_url ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Update Users Statistic', 'wpforo' ); ?></a>&nbsp;
119
+ <a href="<?php echo esc_url( $reset_user_cache ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Delete User Cache', 'wpforo' ); ?></a>&nbsp;
120
+ <a href="<?php echo esc_url( $synch_user_profiles ); ?>" style="min-width:160px; margin-bottom:10px; text-align:center;" class="button button-secondary"><?php _e( 'Synch User Profiles', 'wpforo' ); ?></a>&nbsp;
121
+ </div>
122
+
123
+ </div>
124
+ </div>
125
+ </div><!-- widget / postbox -->
126
+ <?php endif ?>
127
+
128
+ <div class="postbox wpf-dash-box" id="wpforo_dashboard_widget_1">
129
+ <h2 class="wpf-box-header"><span><?php _e( 'General Information', 'wpforo' ); ?></span></h2>
130
+ <div class="inside">
131
+ <div class="main">
132
+ <ul>
133
+ <li class="post-count"><?php _e( 'You are currently running', 'wpforo' ); ?> wpForo <?php echo esc_html( WPFORO_VERSION ) ?></li>
134
+ <li class="page-count"><?php _e( 'Current active theme', 'wpforo' ); ?>: <?php echo WPF()->tpl->theme ?></li>
135
+ <li class="page-count"><?php _e( 'wpForo Community', 'wpforo' ); ?>: <a href="https://wpforo.com/community/">https://wpforo.com/community/</a></li>
136
+ <li class="page-count"><?php _e( 'wpForo Documentation', 'wpforo' ); ?>: <a href="https://wpforo.com/documentation/">https://wpforo.com/documentation/</a></li>
137
+ </ul>
138
+ </div>
139
+ </div>
140
+ </div><!-- widget / postbox -->
141
+
142
+ <?php do_action( 'wpforo_dashboard_widgets_col1' ); ?>
143
+
144
+ </div><!-- normal-sortables -->
145
+ </div><!-- wpforo_postbox_container -->
146
+ <?php endif; ?>
147
+
148
+ <div class="postbox-container" id="postbox-container-3">
149
+ <div class="wpf-box-wrap">
150
+
151
+ <?php do_action( 'wpforo_dashboard_widgets_col3', WPF() ); ?>
152
+
153
+ </div><!-- normal-sortables -->
154
+ </div><!-- wpforo_postbox_container -->
155
+
156
+ </div><!-- dashboard-widgets -->
157
+ </div><!-- dashboard-widgets-wrap -->
158
+
159
+ </div><!-- wpwrap -->
160
+
admin/pages/phrase.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! WPF()->usergroup->can( 'mp' ) ) exit;
5
+
6
+ $wpfaction = wpfval( $_GET, 'wpfaction' );
7
+ ?>
8
+
9
+ <div id="wpf-admin-wrap" class="wrap" style="margin-top: 0">
10
+ <?php wpforo_screen_option() ?>
11
+ <div id="icon-users" class="icon32"><br></div>
12
+ <h2 style="padding:30px 0 0 0; line-height: 20px; margin-bottom:15px;">
13
+ <?php _e( 'Front-end Phrases', 'wpforo' ); ?> &nbsp;
14
+ <a class="add-new-h2" href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) . '&wpfaction=phrase_add_form' ) ?>">
15
+ <?php _e( 'Add New', 'wpforo' ) ?>
16
+ </a>
17
+ </h2>
18
+ <?php WPF()->notice->show() ?>
19
+ <?php
20
+ if( $wpfaction === 'phrase_add_form' ) { ?>
21
+ <form method="POST" id="phrases" class="validate">
22
+ <?php wp_nonce_field( 'wpforo-phrase-add' ); ?>
23
+ <input type="hidden" name="wpfaction" value="phrase_add">
24
+ <table class="form-table">
25
+ <tr>
26
+ <td><?php _e( 'Language', 'wpforo' ) ?></td>
27
+ <td>
28
+ <select name="phrase[langid]">
29
+ <?php WPF()->phrase->show_lang_list(); ?>
30
+ </select>
31
+ </td>
32
+ </tr>
33
+ <tr>
34
+ <td><?php _e( 'Package', 'wpforo' ) ?></td>
35
+ <td>
36
+ <select name="phrase[package]">
37
+ <?php
38
+ if( $packages = WPF()->phrase->get_distinct_packages() ) {
39
+ foreach( $packages as $package ) {
40
+ printf(
41
+ '<option value="%1$s" %2$s>%3$s</option>',
42
+ esc_attr( $package ),
43
+ ( $package === 'wpforo' ) ? 'selected' : '',
44
+ esc_html( $package )
45
+ );
46
+ }
47
+ }
48
+ ?>
49
+ </select>
50
+ </td>
51
+ </tr>
52
+ <tr class="form-field form-required">
53
+ <td>
54
+ <label for="phrase_key"> <?php _e( 'Original', 'wpforo' ) ?></label>
55
+ </td>
56
+ <td>
57
+ <textarea name="phrase[key]" id="phrase_key" required style="min-height: 30px; height: 30px; width: 100%;"></textarea>
58
+ </td>
59
+ </tr>
60
+ <tr>
61
+ <td>
62
+ <label for="phrase_value"> <?php _e( 'Translation', 'wpforo' ) ?></label>
63
+ </td>
64
+ <td>
65
+ <textarea name="phrase[value]" id="phrase_value" required style="min-height: 30px; height: 30px; width: 100%;"></textarea>
66
+ </td>
67
+ </tr>
68
+ <tr>
69
+ <td colspan="2" style="text-align: right">
70
+ <input type="submit" id="createusersub" class="button button-primary" style="padding: 0 30px;" value="<?php _e( 'Save', 'wpforo' ) ?>">
71
+ </td>
72
+ </tr>
73
+ </table>
74
+ </form>
75
+ <?php
76
+ } elseif( $wpfaction === 'phrase_edit_form' ) {
77
+ if( wpfval( $_GET, 'phraseid' ) ) {
78
+ check_admin_referer( 'wpforo-phrase-edit-' . wpfval( $_GET, 'phraseid' ) );
79
+ } else {
80
+ check_admin_referer( 'bulk-phrases' );
81
+ }
82
+ $phraseids = array_merge( (array) wpfval( $_GET, 'phraseid' ), (array) wpfval( $_GET, 'phraseids' ) );
83
+ ?>
84
+ <form method="POST" id="phrases" class="validate">
85
+ <?php wp_nonce_field( 'wpforo-phrases-edit' ); ?>
86
+ <input type="hidden" name="wpfaction" value="phrase_edit">
87
+ <table class="form-table">
88
+ <?php foreach( $phraseids as $phraseid ) : ?>
89
+ <tr class="form-field form-required">
90
+ <th scope="row">
91
+ <?php $phrase = WPF()->phrase->get_phrase( $phraseid ); ?>
92
+ <label for="phrase-<?php echo $phrase['phraseid']; ?>">
93
+ <?php echo esc_html( $phrase['phrase_key'] ); ?>
94
+ </label>
95
+ </th>
96
+ <td>
97
+ <textarea
98
+ name="phrases[<?php echo intval( $phraseid ) ?>]"
99
+ id="phrase-<?php echo $phrase['phraseid']; ?>"
100
+ required
101
+ style="width:80%; min-height:30px; height:30px;"
102
+ ><?php wpfo( $phrase['phrase_value'], true, 'esc_textarea' ); ?></textarea>
103
+ </td>
104
+ </tr>
105
+ <?php endforeach; ?>
106
+ <tr>
107
+ <td colspan="2" style="text-align: right;">
108
+ <input type="submit" id="createusersub" class="button button-primary" style="padding: 0 30px" value="<?php _e( 'Update', 'wpforo' ) ?>">
109
+ </td>
110
+ </tr>
111
+ </table>
112
+ </form>
113
+ <?php
114
+ } elseif( $wpfaction === 'add_new_xml_translation_form' ) {
115
+ ?>
116
+ <form action="" method="POST" name="add_lang" class="validate" enctype="multipart/form-data">
117
+ <?php wp_nonce_field( 'wpforo-settings-language' ); ?>
118
+ <input type="hidden" name="wpfaction" value="add_new_xml_translation">
119
+ <table class="wpforo_settings_table">
120
+ <tbody>
121
+ <tr class="form-field form-required">
122
+ <td>
123
+ <b><label><?php _e( 'Language XML file', 'wpforo' ) ?>:</label></b>
124
+ </td>
125
+ <td>
126
+ <input type="file" name="add_lang[xml]" accept="text/xml">
127
+ </td>
128
+ </tr>
129
+ </tbody>
130
+ </table>
131
+ <div class="wpforo_settings_foot">
132
+ <input type="submit" class="button button-primary" value="<?php _e( 'Add New Language', 'wpforo' ); ?>">
133
+ </div>
134
+ </form>
135
+ <?php
136
+ } else {
137
+ ?>
138
+ <form method="get">
139
+ <input type="hidden" name="page" value="<?php echo wpforo_prefix_slug( 'phrases' ) ?>">
140
+ <?php WPF()->phrase->list_table->languages_dropdown() ?>
141
+ <?php WPF()->phrase->list_table->packages_dropdown() ?>
142
+ <input type="submit" value="<?php _e( 'Filter', 'wpforo' ) ?>" class="button button-large">
143
+
144
+ <?php WPF()->phrase->list_table->search_box( 'Search Phrases', 'wpf-phrase-search' ) ?>
145
+ </form>
146
+ <br>
147
+ <hr>
148
+ <!-- Forms are NOT created automatically, so you need to wrap the table in one to use features like bulk actions -->
149
+ <form id="wpf-dashboard-phrase-page" method="GET">
150
+ <!-- For plugins, we also need to ensure that the form posts back to our current page -->
151
+ <input type="hidden" name="page" value="<?php echo wpforo_prefix_slug( 'phrases' ) ?>">
152
+ <input type="hidden" name="wpfaction" value="phrase_edit_form">
153
+
154
+ <!-- Now we can render the completed list table -->
155
+ <?php WPF()->phrase->list_table->display() ?>
156
+ </form>
157
+
158
+
159
+ <hr style="margin-top: 30px;">
160
+
161
+ <form method="post">
162
+ <?php wp_nonce_field( 'wpforo-phrases-change-language' ); ?>
163
+ <input type="hidden" name="wpfaction" value="phrases_change_lang">
164
+ <table>
165
+ <tr>
166
+ <td style="padding-bottom: 10px;">
167
+ <label for="langid" style="font-weight: bold;"><?php _e( 'Manage Phrasees with XML File', 'wpforo' ); ?> <a href="https://wpforo.com/docs/wpforo-v2/wpforo-settings/general-settings/#xml-language" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"><i class="far fa-question-circle"></i></a></label>
168
+ <p class="wpf-info"><?php _e( 'This option is only related to XML language files. You should upload a translation XML file to have a new language option in this drop-down. If you are using PO/MO translation files you should change WordPress Language in Dashboard > Settings admin page to load according translation for wpForo.', 'wpforo' ); ?></p>
169
+ </td>
170
+ </tr>
171
+ <tr>
172
+ <td style="display: flex; justify-content: flex-start; align-items: center;">
173
+ <select id="langid" name="langid" style="float:left;">
174
+ <?php WPF()->phrase->show_lang_list() ?>
175
+ </select>
176
+ <input type="submit" class="button button-primary" value="<?php _e( 'Change', 'wpforo' ) ?>">
177
+ </td>
178
+ </tr>
179
+ <tr>
180
+ <td style="padding-top: 10px;">
181
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) . '&wpfaction=add_new_xml_translation_form' ) ?>" class="add-new-h2"><?php _e( 'Add New', 'wpforo' ); ?></a>
182
+ </td>
183
+ </tr>
184
+ </table>
185
+ </form>
186
+ <?php
187
+ }
188
+ ?>
189
+ </div>
admin/pages/settings.php ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! WPF()->usergroup->can( 'ms' ) ) exit;
5
+ $tab = sanitize_key( (string) wpfval( $_GET, 'wpf_tab' ) );
6
+ WPF()->settings->init_info();
7
+ ?>
8
+
9
+ <div class="wrap" style="margin:10px 20px 0 20px">
10
+ <div id="wpf-setbox" class="wpf-dash">
11
+ <!-- wpf-setbox-head start -->
12
+ <div class="wpf-setbox-head">
13
+ <div class="wpf-head-logo">
14
+ <img src="<?php echo esc_url_raw( WPFORO_URL . "/assets/images/dashboard/wpforo-logo.png" ); ?>" alt="wpForo Logo">
15
+ </div>
16
+ <div class="wpf-head-title">
17
+ <?php //esc_html_e("wpForo", "wpforo") ?>
18
+ </div>
19
+ <div class="wpf-head-info">
20
+ <span><a href="https://wpforo.com/docs/wpforo-v2/" target="_blank"><?php esc_html_e( "Documentation", "wpforo" ); ?></a></span>
21
+ <span><a href="https://wpforo.com/community/" target="_blank"><?php esc_html_e( "Support", "wpforo" ); ?></a></span>
22
+ <span><a href="https://gvectors.com/product-category/wpforo/" target="_blank"><?php esc_html_e( "Addons", "wpforo" ); ?></a></span>
23
+ </div>
24
+ </div>
25
+ <h1 style="width:0;height:0;margin:0;padding:0;"></h1>
26
+ <?php WPF()->notice->show(); ?>
27
+ <?php do_action( 'wpforo_settings_page_top' ) ?>
28
+ <?php do_action( "wpforo_option_page" ); ?>
29
+ <?php settings_errors( "wpforo" ); ?>
30
+ <!-- wpf-setbox-head end -->
31
+
32
+ <!-- wpf-setbox-body start -->
33
+ <div id="wpf-admin-wrap" class="wpf-setbox-body">
34
+ <div class="wpf-section">
35
+ <h3><?php esc_html_e( "Forum Settings", "wpforo" ) ?></h3>
36
+ <div class="wpf-opt-search">
37
+ <input id="wpf-opt-search-field" type="text" name="" value="" placeholder="<?php esc_attr_e( "Find an option...", "wpforo" ) ?>"/>
38
+ <span class="dashicons dashicons-search"></span>
39
+ <div id="wpf-opt-search-results"></div>
40
+ </div>
41
+ </div>
42
+
43
+ <?php if( ! $tab || ( ! isset( WPF()->settings->info->core[ $tab ] ) && ! isset( WPF()->settings->info->addons[ $tab ] ) ) ) : ?>
44
+ <!-- wpf-box-wrap start -->
45
+ <div class="wpf-box-wrap wpf-settings-home">
46
+ <?php
47
+ foreach( WPF()->settings->info->core as $tab_key => $setting ) {
48
+ if( ( $_GET['page'] === 'wpforo-base-settings' && ! $setting['base'] && is_wpforo_multiboard() ) || ( $_GET['page'] !== 'wpforo-base-settings' && $setting['base'] && is_wpforo_multiboard() ) ) {
49
+ continue;
50
+ }
51
+ ?>
52
+ <!-- Settings start -->
53
+ <div class="wpf-box">
54
+ <div class="wpf-box-info wpf-<?php echo esc_attr( $setting["status"] ); ?>">
55
+ <img src="<?php echo esc_url_raw( WPFORO_URL . "/assets/images/dashboard/" . $setting["status"] . ".png" ); ?>">
56
+ </div>
57
+ <div class="wpf-box-head">
58
+ <a href="<?php echo esc_url_raw( admin_url( "admin.php?page=" . $_GET['page'] . "&wpf_tab=" . $tab_key ) ); ?>" title="<?php esc_attr_e( "Open Settings", "wpforo" ) ?>">
59
+ <?php echo $setting["icon"]; ?>
60
+ </a>
61
+ </div>
62
+ <div class="wpf-box-foot">
63
+ <div class="wpf-box-title">
64
+ <a href="<?php echo esc_url_raw( admin_url( "admin.php?page=" . $_GET['page'] . "&wpf_tab=" . $tab_key ) ); ?>" title="<?php esc_attr_e( "Open Settings", "wpforo" ) ?>">
65
+ <?php echo $setting["title"] ?>
66
+ </a>
67
+ </div>
68
+ <div class="wpf-box-arrow">
69
+ <img src="<?php echo esc_url_raw( WPFORO_URL . "/assets/images/dashboard/arrow-right.png" ); ?>">
70
+ </div>
71
+ </div>
72
+ </div>
73
+ <!-- Settings end -->
74
+ <?php
75
+ }
76
+ ?>
77
+ <div class="wpf-clear"></div>
78
+ </div>
79
+ <!-- wpf-box-wrap end -->
80
+ <?php if( WPF()->settings->info->addons ): ?>
81
+ <div class="wpf-section">
82
+ <h3><?php esc_html_e( "Addons Settings", "wpforo" ) ?></h3>
83
+ </div>
84
+ <!-- wpf-box-wrap start -->
85
+ <div class="wpf-box-wrap wpf-box-addons wpf-settings-home">
86
+ <?php foreach( WPF()->settings->info->addons as $addon_key => $addon ) {
87
+ if( ( $_GET['page'] === 'wpforo-base-settings' && ! $addon['base'] && is_wpforo_multiboard() ) || ( $_GET['page'] !== 'wpforo-base-settings' && $addon['base'] && is_wpforo_multiboard() ) ) {
88
+ continue;
89
+ }
90
+ ?>
91
+ <!-- Settings start -->
92
+ <div class="wpf-box">
93
+ <div class="wpf-box-info wpf-<?php echo esc_attr( $addon["status"] ); ?>">
94
+ <img src="<?php echo esc_url_raw( WPFORO_URL . "/assets/images/dashboard/" . $addon["status"] . ".png" ); ?>" alt="">
95
+ </div>
96
+ <div class="wpf-box-head">
97
+ <a href="<?php echo esc_url_raw( admin_url( "admin.php?page=" . $_GET['page'] . "&wpf_tab=" . $addon_key ) ); ?>" title="<?php esc_attr_e( "Open Settings", "wpforo" ) ?>">
98
+ <?php echo $addon["icon"]; ?>
99
+ </a>
100
+ </div>
101
+ <div class="wpf-box-foot">
102
+ <div class="wpf-box-title">
103
+ <a href="<?php echo esc_url_raw( admin_url( "admin.php?page=" . $_GET['page'] . "&wpf_tab=" . $addon_key ) ); ?>" title="<?php esc_attr_e( "Open Settings", "wpforo" ) ?>">
104
+ <?php echo $addon["title"] ?>
105
+ </a>
106
+ </div>
107
+ <div class="wpf-box-arrow">
108
+ <img src="<?php echo esc_url_raw( WPFORO_URL . "/assets/images/dashboard/arrow-right.png" ); ?>" alt="">
109
+ </div>
110
+ </div>
111
+ </div>
112
+ <!-- Settings end -->
113
+ <?php
114
+ } ?>
115
+ </div>
116
+ <!-- wpf-box-wrap end -->
117
+ <div class="wpf-clear"></div>
118
+ <?php endif; ?>
119
+ <?php else: ?>
120
+ <div id="wpf-settings-tab" class="wpf-box-wrap wpf-settings-tab_<?php echo esc_attr( $tab ); ?>" style="align-items:flex-start;">
121
+ <!-- Settings Content start -->
122
+ <div class="wpf-box wpf-setcon">
123
+ <?php $setting = ( wpfkey( WPF()->settings->info->core, $tab ) ) ? WPF()->settings->info->core[ $tab ] : WPF()->settings->info->addons[ $tab ]; ?>
124
+ <div class="wpf-setcon-head">
125
+ <?php echo esc_html( $setting["title"] ); ?>
126
+ <a class="wpf-back" href="<?php echo esc_url_raw( admin_url( "admin.php?page=" . wpfval( $_GET, 'page' ) ) ); ?>"><span class="dashicons dashicons-arrow-left-alt2"></span><?php esc_html_e( "Back", "wpforo" ) ?></a>
127
+ </div>
128
+ <div class="wpf-setcon-body">
129
+ <?php
130
+ if( wpfval($setting, 'with_custom_form') ){
131
+ if( is_callable( $setting['callback_for_page'] ) ) call_user_func( $setting['callback_for_page'], $setting );
132
+ do_action( "wpforo_settings_after", $tab, $setting );
133
+ }else{
134
+ ?>
135
+ <form method="POST" enctype="multipart/form-data">
136
+ <?php
137
+ wp_nonce_field( "wpforo_settings_save_" . sanitize_title( $tab ) );
138
+ if( is_callable( $setting['callback_for_page'] ) ) call_user_func( $setting['callback_for_page'], $setting );
139
+ do_action( "wpforo_settings_after", $tab, $setting );
140
+ ?>
141
+ <div class="wpf-opt-row" style="flex-wrap: wrap; margin-top: 50px; display: flex;">
142
+ <div style="display: flex;">
143
+ <input style="padding-left: 21px; padding-right: 21px;" type="submit" class="button button-primary" name="save" value="<?php esc_attr_e( "Save Options", "wpforo" ); ?>">
144
+ <?php if( ! $setting['base'] && is_wpforo_multiboard() ) : ?>
145
+ <input style="order: -1;" type="submit" class="button button-primary" name="save_for_all" value="<?php esc_attr_e( "Save Options for All Boards", "wpforo" ); ?>">
146
+ <?php endif; ?>
147
+ </div>
148
+ <div style="order: -1;">
149
+ <input type="submit" class="button" value="<?php _e( 'Reset Options', 'wpforo' ); ?>" name="reset" onclick="return confirm('<?php wpforo_phrase( 'Do you really want to reset options?' ) ?>')"/>
150
+ </div>
151
+ </div>
152
+ </form>
153
+ <?php do_action( "wpforo_settings_after_form", $tab, $setting ); ?>
154
+ <?php
155
+ }
156
+ ?>
157
+ </div>
158
+ </div>
159
+ <!-- Settings Content end -->
160
+ <!-- Settings Sidebar start -->
161
+ <div class="wpf-setbar">
162
+ <ul class="wpf-box wpf-menu-group">
163
+ <li class="wpf-menu-head"><?php esc_html_e( "Settings", "wpforo" ) ?> <span class="dashicons dashicons-arrow-up"></span></li>
164
+ <?php foreach( WPF()->settings->info->core as $tab_key => $setting ): ?>
165
+ <li<?php if( $tab === $tab_key ) echo " class='wpf-active'"; ?>><a href="<?php echo esc_url_raw( admin_url( "admin.php?page=" . ( ( $setting['base'] && is_wpforo_multiboard() ) ? 'wpforo-base-settings' : wpforo_prefix_slug( 'settings' ) ) . "&wpf_tab=" . $tab_key ) ); ?>"><span class="dashicons dashicons-arrow-left-alt2"></span> <?php echo esc_html( $setting["title"] ); ?></a></li>
166
+ <?php endforeach; ?>
167
+ </ul>
168
+ <?php if( WPF()->settings->info->addons ): ?>
169
+ <ul class="wpf-box wpf-menu-group">
170
+ <li class="wpf-menu-head"><?php esc_html_e( "Addons Settings", 'wpforo' ) ?> <span class="dashicons dashicons-arrow-up"></span></li>
171
+ <?php foreach( WPF()->settings->info->addons as $addon_key => $addon ): ?>
172
+ <li<?php if( $tab === $addon_key ) echo " class='wpf-active'"; ?>><a href="<?php echo esc_url_raw( admin_url( "admin.php?page=" . ( ( $addon['base'] && is_wpforo_multiboard() ) ? 'wpforo-base-settings' : wpforo_prefix_slug( 'settings' ) ) . "&wpf_tab=" . $addon_key ) ); ?>"><span class="dashicons dashicons-arrow-left-alt2"></span> <?php echo esc_html( $addon["title"] ); ?></a></li>
173
+ <?php endforeach; ?>
174
+ </ul>
175
+ <?php endif; ?>
176
+ </div>
177
+ <!-- Settings Sidebar end -->
178
+ <div class="wpf-clear"></div>
179
+ </div>
180
+ <script>
181
+ jQuery(document).ready(function ($) {
182
+ $(document).on('click', '.wpf-setbar .wpf-menu-head .dashicons-arrow-down, .wpf-setbar .wpf-menu-head .dashicons-arrow-up', function () {
183
+ var $this = $(this)
184
+ var up = $this.hasClass('dashicons-arrow-up')
185
+ $this.toggleClass('dashicons-arrow-down dashicons-arrow-up')
186
+ if (up) {
187
+ $this.parents('.wpf-menu-group').find('li:not(.wpf-menu-head)').hide()
188
+ } else {
189
+ $this.parents('.wpf-menu-group').find('li:not(.wpf-menu-head)').show()
190
+ }
191
+ })
192
+ })
193
+ </script>
194
+ <?php endif; ?>
195
+ </div>
196
+ <!-- wpf-setbox-body end -->
197
+
198
+ </div>
199
+ </div>
admin/pages/themes.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! WPF()->usergroup->can( 'mth' ) ) exit;
5
+ ?>
6
+
7
+ <div id="wpf-admin-wrap" class="wrap wpforo-themes">
8
+ <h2 style="padding:20px 0 0; line-height: 20px; margin-bottom:15px;">
9
+ <?php _e( 'Forum Themes', 'wpforo' ); ?>
10
+ </h2>
11
+ <?php WPF()->notice->show() ?>
12
+
13
+ <div style="box-shadow:none; margin:25px 0 20px;" class="wpf-info-bar">
14
+ <p style="font-size:13px; padding:0; margin:10px;">
15
+ wpForo theme files contain the markup and template structure for frontend of your forum.
16
+ Theme files can be found within the <span style="color:#43A6DF">/wpforo/themes/</span> directory, in current active theme folder, for example <span style="color:#43A6DF">/classic/</span>.
17
+ You can edit these files in an upgrade-safe way using overrides.
18
+ For example, copy the certain or all files of <span style="color:#43A6DF">/classic/</span> folder into a folder within your current active WordPress theme named <span style="color:#43A6DF">/wpforo/</span>, keeping the same file structure.<br>
19
+ </p>
20
+ <div style="background:#F5F5F5; border:#ddd 1px dotted; padding:10px 15px; margin:5px 0; font-size:13px; line-height:18px;">Example: To override the "Topic List" template file of the Extended (#1) forum layout, copy according file: <span style="color:#43A6DF">plugins/wpforo/themes/classic/<span style="color:#C98B4C">layouts/1/topic.php</span></span> to <span style="color:#43A6DF">themes/yourtheme/wpforo/<span style="color:#C98B4C">layouts/1/topic.php</span></span></div>
21
+ <p style="font-size:13px; padding:0; margin:10px;">
22
+ The copied file will now automatically override the wpForo default theme file. All changes in this file will not be lost on forum update.
23
+ </p>
24
+ <div style="background:#F6F7D8; border:#ddd 1px dotted; padding:10px 15px; margin:5px 0; font-size:13px; line-height:18px;">Do not edit these files within the core plugin itself as they are overwritten during the upgrade process and any customizations will be lost.</div>
25
+ </div>
26
+
27
+ <?php
28
+ $themes = WPF()->tpl->find_themes();
29
+ if( ! empty( $themes ) ) :
30
+ $_tk = WPF()->tpl->theme . '/style.css';
31
+ $_t = $themes[$_tk];
32
+ unset( $themes[$_tk] );
33
+ $themes = array_merge( [ $_tk => $_t], $themes );
34
+ foreach( $themes as $main_file => $theme ):
35
+ $theme_folder = trim( basename( dirname( $main_file ) ), '/' );
36
+ $theme_url = WPFORO_THEME_URL . '/' . $theme_folder;
37
+ $layouts = WPF()->tpl->find_themes( '/' . $theme_folder . '/layouts', 'php', 'layout' );
38
+ $styles = WPF()->tpl->find_styles( $theme_folder );
39
+ $is_active = WPF()->tpl->theme === $theme_folder;
40
+ $theme_archive = wpforo_get_option( 'theme_archive_' . $theme_folder, [], false );
41
+ $has_archive = ! empty( $theme_archive );
42
+ ?>
43
+ <div class="wpf-div-table">
44
+ <div class="wpf-div-tr">
45
+ <div class="wpf-div-td" style="width:50%; border-bottom:1px dotted #ddd;">
46
+ <?php if( $is_active ): ?>
47
+ <span style="color:#279800; font-weight:bold; text-transform:uppercase;"><?php _e( 'Current active theme', 'wpforo' ); ?></span>
48
+ <?php else: ?>
49
+ <span style="color:#555555; font-weight:bold; text-transform:uppercase;"><?php _e( 'Inactive', 'wpforo' ); ?></span>
50
+ <?php endif; ?>
51
+ </div>
52
+ <div class="wpf-div-td" style="width:50%; border-bottom:1px dotted #ddd;">
53
+ <?php _e( 'LAYOUTS', 'wpforo' ); ?> (<?php echo count( $layouts ) ?>)
54
+ </div>
55
+ </div>
56
+ <div class="wpf-div-tr">
57
+ <div class="wpf-div-td" style="width:60%;">
58
+ <div class="wpf-theme-screenshot" style="background:url('<?php echo esc_url( $theme_url ) ?>/screenshot.png') 0 0 no-repeat;background-size: cover;"></div>
59
+ <div class="wpf-theme-info">
60
+ <h3 style="margin-top:5px; margin-bottom:10px;"><?php echo esc_html( wpforo_text( $theme['name']['value'], 30, false ) ) ?> | <?php echo ( $theme['version']['value'] ) ? 'version ' . esc_html( $theme['version']['value'] ) : ''; ?></h3>
61
+ <p style="font-size:14px;" title="<?php echo esc_attr( $theme['author']['value'] ) ?>"><?php echo ( $theme['author']['value'] ) ? '<strong>Author:</strong>&nbsp; ' . esc_html( wpforo_text( $theme['author']['value'], 30, false ) ) : ''; ?></p>
62
+ <p style="font-size:14px;" title="<?php echo esc_attr( $theme['theme_url']['value'] ) ?>"><?php echo ( $theme['theme_url']['value'] ) ? '<strong>URI:</strong>&nbsp; <a href="' . esc_url( $theme['theme_url']['value'] ) . '" target="_blank">' . mb_substr( $theme['theme_url']['value'], 0, 30 ) . '</a>' : ''; ?></p>
63
+ <p style="margin-top:5px;"><?php echo ( $theme['description']['value'] ) ? esc_html( wpforo_text( $theme['description']['value'], 200, false ) ) : ''; ?></p>
64
+ </div>
65
+ <div class="wpf-theme-actions">
66
+ <?php if( count( $themes ) > 1 && ! $is_active ): ?>
67
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=wpforo-themes&wpfaction=theme_activate&theme=' . sanitize_text_field( $theme_folder ) ), 'wpforo-theme-activate' ) ?>" class="wpf-action button"><?php _e( 'Activate', 'wpforo' ); ?></a>
68
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=wpforo-themes&wpfaction=theme_delete&theme=' . sanitize_text_field( $theme_folder ) ), 'wpforo-theme-delete' ) ?>" class="wpf-delete button" onclick="return confirm('<?php _e( 'Are you sure you want to delete this theme files?' ) ?>')"><?php _e( 'Delete', 'wpforo' ); ?></a>
69
+ <?php endif; ?>
70
+ </div>
71
+ </div>
72
+ <div class="wpf-div-td" style="width:40%; border-left: 1px dotted #ddd;">
73
+ <?php
74
+ if( ! empty( $layouts ) ) {
75
+ foreach( $layouts as $layout ) {
76
+ ?>
77
+ <div class="wpf-layout-info" style="display:block; border-bottom:1px dotted #ddd; padding:0 0 10px; margin:0 0 10px;">
78
+ <h4 style="margin:1px 0;"><?php echo esc_html( wpforo_text( $layout['name']['value'], 30, false ) ) ?> <?php echo ( $layout['version']['value'] ) ? '(' . esc_html( $layout['version']['value'] ) . ')' : ''; ?> | <?php echo ( $layout['author']['value'] ) ? '<a href="' . esc_url( $layout['layout_url']['value'] ) . '" target="_blank">' . esc_html( wpforo_text( $layout['author']['value'], 25, false ) ) . '</a>' : '' ?></h4>
79
+ <p><?php echo ( $layout['description']['value'] ) ? esc_html( wpforo_text( $layout['description']['value'], 120, false ) ) : ''; ?></p>
80
+ </div>
81
+ <?php
82
+ }
83
+ } else {
84
+ ?>
85
+ <div class="wpf-layout-info"><p style="text-align:center;"><? _e( 'No layout found', 'wpforo' ); ?></p></div><?php
86
+ }
87
+ ?>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ <?php endforeach; ?>
92
+ <?php else: ?>
93
+ <div class="wpf-div-table">
94
+ <div class="wpf-div-tr">
95
+ <div class="wpf-div-td">
96
+ <p style="text-align:center;"><?php _e( 'No theme found', 'wpforo' ); ?></p>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ <?php endif; ?>
101
+ </div>
admin/pages/tools.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( 'ABSPATH' ) || ! WPF()->usergroup->can( 'mt' ) ) die ?>
2
+
3
+ <div class="wrap"><h2 style="padding:0 0 30px 0; line-height: 20px;"><?php _e( 'Forum Tools', 'wpforo' ) ?></h2></div>
4
+ <?php WPF()->notice->show() ?>
5
+ <?php do_action( 'wpforo_tools_page_top' ) ?>
6
+ <div id="wpf-admin-wrap" class="wrap">
7
+ <div id="icon-users" class="icon32"><br></div>
8
+ <?php
9
+ $tabs = [
10
+ 'debug' => __( 'Debug', 'wpforo' ),
11
+ 'tables' => __( 'Database Tables', 'wpforo' ),
12
+ 'misc' => __( 'Admin Note', 'wpforo' )
13
+ ];
14
+ wpforo_admin_tools_tabs( $tabs, ( isset( $_GET['tab'] ) ? $_GET['tab'] : 'debug' ) );
15
+ ?>
16
+ <div class="wpf-info-bar" style="padding:1%">
17
+ <?php
18
+ $includefile = WPFORO_DIR . '/admin/tools-tabs/debug.php';
19
+ if( ! empty( $_GET['tab'] ) ) {
20
+ switch( $_GET['tab'] ) {
21
+ case 'misc':
22
+ $includefile = WPFORO_DIR . '/admin/tools-tabs/misc.php';
23
+ break;
24
+ case 'tables':
25
+ $includefile = WPFORO_DIR . '/admin/tools-tabs/tables.php';
26
+ break;
27
+ }
28
+ }
29
+ include_once( $includefile );
30
+ ?>
31
+ </div>
32
+ </div>
admin/pages/usergroup.php ADDED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! WPF()->usergroup->can( 'vmg' ) ) exit;
5
+
6
+ $wpfaction = wpfval( $_GET, 'wpfaction' );
7
+ ?>
8
+
9
+ <div id="wpf-admin-wrap" class="wrap">
10
+ <div id="icon-users" class="icon32"><br/></div>
11
+ <h2 style="padding:30px 0 10px;line-height: 20px;">
12
+ <?php _e( 'Usergroups', 'wpforo' ) ?>
13
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&wpfaction=wpforo_usergroup_save_form' ) ?>" class="add-new-h2">
14
+ <?php _e( 'Add New', 'wpforo' ) ?>
15
+ </a>
16
+ </h2>
17
+ <?php WPF()->notice->show() ?>
18
+ <!-- ############################################################### Usergroup Main Form -->
19
+
20
+ <?php if( ! $wpfaction ) : ?>
21
+ <br/>
22
+ <form method="POST" class="wpforo-ajax-form">
23
+ <table id="usergroup_table" class="wp-list-table widefat fixed posts">
24
+ <thead>
25
+ <tr>
26
+ <?php if( wpforo_setting( 'authorization', 'role_synch' ) ): ?>
27
+ <th scope="col" id="role" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;">
28
+ <span>
29
+ <?php _e( 'User Role', 'wpforo' ) ?> &nbsp;
30
+ <a href="https://codex.wordpress.org/Roles_and_Capabilities" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;">
31
+ <i class="far fa-question-circle"></i>
32
+ </a>
33
+ </span>
34
+ </th>
35
+ <?php endif; ?>
36
+ <th scope="col" id="title" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Usergroup', 'wpforo' ) ?> &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/members/usergroups-and-permissions/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span></th>
37
+ <th scope="col" id="count" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Members', 'wpforo' ) ?></span></th>
38
+ <th scope="col" id="default" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Default', 'wpforo' ) ?></span></th>
39
+ <th scope="col" id="access" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Default Access', 'wpforo' ) ?></span></th>
40
+ <th scope="col" id="color" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Color', 'wpforo' ) ?></span></th>
41
+ <th scope="col" id="id" class="manage-column column-title" style="padding:10px; width: 4%; font-size:14px; padding-left:15px; font-weight:bold; text-align: center;"><span><?php _e( 'ID', 'wpforo' ) ?></span></th>
42
+ </tr>
43
+ </thead>
44
+ <tbody id="the-list">
45
+ <?php $groups = WPF()->usergroup->usergroup_list_data(); ?>
46
+ <?php foreach( $groups as $group ) : ?>
47
+ <tr id="usergroup-<?php echo intval( $group['groupid'] ) ?>" class="format-standard hentry alternate iedit" valign="top">
48
+ <?php if( wpforo_setting( 'authorization', 'role_synch' ) ): ?>
49
+ <td class="post-title page-title column-title" style="border-bottom:1px dotted #CCCCCC; padding-left:20px;">
50
+ <?php
51
+ $ug_role = trim( $group['role'] );
52
+ $ug_role = ( wp_roles()->is_role( $ug_role ) ) ? $ug_role = '<a href="' . admin_url( 'users.php?role=' . $group['role'] ) . '" title="' . __( 'View Users', 'wpforo' ) . '" target="_blank"><i class="far fa-user"></i></a>' : '';
53
+ ?>
54
+ <span style="font-size: 16px; vertical-align: middle;"><?php echo $ug_role; ?></span> &nbsp;
55
+ <?php if( $group['groupid'] != 4 && $group['groupid'] != 1 ) : ?>
56
+ <select name="wpf_synch_roles[<?php echo $group['groupid'] ?>]" style="background:#FDFDFD; display:inline; max-width: 80%;">
57
+ <?php $selected = ( wpfval( $group, 'role' ) ) ? $group['role'] : 'subscriber'; ?>
58
+ <?php wp_dropdown_roles( $selected ); ?>
59
+ </select>
60
+ <?php elseif( $group['groupid'] == 1 ): ?>
61
+ <span style="font-size: 14px; vertical-align: bottom;">&nbsp;<?php echo $group['role']; ?></span>
62
+ <input type="hidden" name="wpf_synch_roles[1]" value="administrator">
63
+ <?php endif; ?>
64
+ </td>
65
+ <?php endif; ?>
66
+ <td class="post-title page-title column-title" style="border-bottom:1px dotted #CCCCCC; padding-left:20px; background:#ffffff;">
67
+ <?php $edit_url = ( $group['groupid'] != 1 ? admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&groupid=' . $group['groupid'] . '&wpfaction=wpforo_usergroup_save_form' ) : '#' ) ?>
68
+ <strong>
69
+ <a class="row-title" href="<?php echo esc_url( $edit_url ) ?>" title="<?php _e( 'Usergroup Name', 'wpforo' ) ?>">
70
+ <?php echo esc_html( $group['name'] ) ?>
71
+ </a> <?php if( wpfval( $group, 'secondary' ) ): ?>&nbsp;<span style="font-size: 12px; color: #777777;" title="<?php _e( 'Also used as Secondary Usergroup' ); ?>"><i class="fas fa-pause"></i></span><?php endif; ?>
72
+ </strong>
73
+ <div class="row-actions">
74
+ <span class="edit"><a title="<?php _e( 'Edit this usergroup', 'wpforo' ) ?>" href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&groupid=' . intval( $group['groupid'] ) . '&wpfaction=wpforo_usergroup_save_form' ) ?>"><?php _e( 'Edit', 'wpforo' ) ?></a> |</span>
75
+ <?php if( $group['groupid'] > 5 ): ?>
76
+ <span class="trash"><a class="submitdelete" title="<?php _e( 'Delete this usergroup', 'wpforo' ) ?>" href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&groupid=' . intval( $group['groupid'] ) . '&wpfaction=usergroup_delete_form' ) ?>"><?php _e( 'Delete', 'wpforo' ) ?></a> |</span>
77
+ <?php endif; ?>
78
+ <span class="view"><a title="<?php _e( 'View users list in this usergroup', 'wpforo' ) ?>" href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'members' ) . '&filter_by_group=' . intval( $group['groupid'] ) ) ?>" rel="permalink"><?php _e( 'View', 'wpforo' ) ?></a></span>
79
+ </div>
80
+ </td>
81
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#ffffff;">
82
+ <?php if( $group['groupid'] != 4 ) : ?>
83
+ <strong><a class="row-title" href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'members' ) . '&filter_by_group=' . intval( $group['groupid'] ) ) ?>" title="<?php _e( 'The number of forum members with this usergroup. Click to view members.', 'wpforo' ) ?>"><i class="fas fa-user"></i>&nbsp; <?php echo intval( $group['count'] ) ?></a></strong>
84
+ <?php endif; ?>
85
+ </td>
86
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#ffffff;">
87
+ <strong style="color: #00a636">
88
+ <?php if( $group['groupid'] == WPF()->usergroup->default_groupid ) : _e( 'is Default', 'wpforo' ); ?>
89
+ <?php elseif( $group['groupid'] == 4 || $group['groupid'] == 1 ) : ?>
90
+ <?php else : ?>
91
+ <a class="row-title" href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&wpfaction=default_groupid_change&default_groupid=' . intval( $group['groupid'] ) ), 'wpforo-default-groupid-change-' . intval( $group['groupid'] ) ) ?>" title="<?php _e( 'Users get the Default Usergroup on registration', 'wpforo' ) ?>"><?php _e( 'Set as Default', 'wpforo' ) ?></a>
92
+ <?php endif ?>
93
+ </strong>
94
+ </td>
95
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#ffffff;">
96
+ <strong><?php echo $group['access'] ?></strong>
97
+ </td>
98
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#ffffff;">
99
+ <strong><?php if( ! isset( $group['color'] ) || ! $group['color'] ): ?><?php _e( 'default (#15)', 'wpforo' ) ?><?php else: ?><input type="color" value="<?php echo $group['color'] ?>" disabled /><?php endif; ?></strong>
100
+ </td>
101
+ <td class="post-title page-title column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; background:#ffffff; text-align: center;">
102
+ <strong><?php echo $group['groupid'] ?></strong>
103
+ </td>
104
+ </tr>
105
+ <?php endforeach ?>
106
+ </tbody>
107
+ <tfoot>
108
+ <tr>
109
+ <?php if( wpforo_setting( 'authorization', 'role_synch' ) ): ?>
110
+ <td style="text-align: center;">
111
+ <button class="button button-primary wpf-synch-roles" title="<?php _e( 'Synchronize Users Usergroups and Roles', 'wpforo' ) ?>" style="margin: 5px auto;">
112
+ <?php _e( 'Synchronize', 'wpforo' ) ?>
113
+ <i class="fas wpf-spinner"></i>
114
+ </button>
115
+ <?php
116
+ $wpf_nonce = wp_create_nonce( 'wpforo_synch_roles' );
117
+ $wpf_data = wpforo_get_option( 'synch_roles', [] );
118
+ $wpf_id = ( wpfval( $wpf_data, 'id' ) ) ? intval( $wpf_data['id'] ) : 0;
119
+ $wpf_start = ( wpfval( $wpf_data, 'start' ) ) ? intval( $wpf_data['start'] ) : 0;
120
+ $wpf_step = ( wpfval( $wpf_data, 'step' ) ) ? intval( $wpf_data['step'] ) : 1;
121
+ $wpf_left = ( wpfval( $wpf_data, 'left' ) ) ? intval( $wpf_data['left'] ) : 0;
122
+ ?>
123
+ <input type="hidden" name="wpf-start-id" value="<?php echo intval( $wpf_id ) ?>" class="wpf-start-id"/>
124
+ <input type="hidden" name="wpf-start" value="<?php echo intval( $wpf_start ) ?>" class="wpf-start"/>
125
+ <input type="hidden" name="wpf-step" value="<?php echo intval( $wpf_step ) ?>" class="wpf-step"/>
126
+ <input type="hidden" name="wpf-left-users" value="<?php echo intval( $wpf_left ) ?>" class="wpf-left-users"/>
127
+ </td>
128
+ <?php endif; ?>
129
+ <td colspan="6" style="text-align:left; vertical-align: middle; padding:10px 20px;"><span class="wpf-progress" style="font-size: 14px; display: inline-block; font-weight: 600; line-height: 18px;">&nbsp;</span></td>
130
+ </tr>
131
+ </tfoot>
132
+ </table>
133
+ </form>
134
+
135
+ <?php if( wpforo_setting( 'authorization', 'role_synch' ) ): ?>
136
+ <p class="description" style="box-shadow: 1px 1px 6px #cccccc; background: #f7f7f7; padding: 20px; width: 95%; margin: 20px 0 0; font-size: 14px;">
137
+ <span style="color: #ff4b3c; font-weight: 600;"><?php _e( 'Note:', 'wpforo' ) ?></span>
138
+ <?php _e( 'The [Synchronize] button changes all users Usergroups according to the users Roles. For example, if you select "Contributor" Role for "Registered" Usergroup, all users with "Contributor" Role will get "Registered" Usergroup in forum. The synchronization process may take a few seconds or dozens of minutes, it depends on the number of users. Please be patient, don\'t close this page and wait until the progress counter says 100% completed.', 'wpforo' ); ?>
139
+ </p>
140
+ <?php endif; ?>
141
+
142
+ <p id="synch" style="margin: 4px;">&nbsp;</p>
143
+
144
+ <?php if( wpforo_setting( 'authorization', 'role_synch' ) ): ?>
145
+ <?php $roles_wug = WPF()->usergroup->get_roles();
146
+ $roles_ug = WPF()->usergroup->get_roles_ug();
147
+ $roles_users = count_users(); ?>
148
+ <?php if( ! empty( $roles_wug ) ): ?>
149
+ <h1 style="padding-bottom: 0; width: 97%; margin-bottom: 15px;">&nbsp;<?php _e( 'User Roles' ); ?></h1>
150
+ <p class="description" style="border-top: 1px solid #cccccc; background: #f7f7f7; padding: 15px 20px; width: 95%; margin: 10px 0 20px 0; font-size: 14px;">
151
+ <?php _e(
152
+ 'In the table above (Usergroups) you can see the list of all available forum Usergroups. The first column of this table displays selected User Roles which are synched with certain Usergroup. However some User Roles are still not synced with any Usergroup of your forum. You can find not-synced User Roles in the table below (User Roles). If you use any of these not-synced User Roles and you want to grant some forum accesses to users of these User Roles you should create new Usergroups for each of them. Once new Usergroup is created, you should use the [Synchronize] button in the table above to synchronize User Roles with new Usergroups.'
153
+ ); ?>
154
+ </p>
155
+ <table id="usergroup_table" class="wp-list-table widefat fixed posts">
156
+ <thead>
157
+ <tr>
158
+ <th scope="col" id="role" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Role Name', 'wpforo' ) ?></span></th>
159
+ <th scope="col" id="id" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'User Role', 'wpforo' ) ?></span></th>
160
+ <th scope="col" id="title" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Usergroups', 'wpforo' ) ?></span></th>
161
+ <th scope="col" id="count" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Users', 'wpforo' ) ?></span></th>
162
+ <th scope="col" id="default" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold; width: 40%;"><span><?php _e( 'Status', 'wpforo' ) ?></th>
163
+ </tr>
164
+ </thead>
165
+ <tbody id="the-list">
166
+ <?php foreach( $roles_wug as $role_key => $role_name ): ?>
167
+ <tr>
168
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#F9F9F9; font-weight: 600; font-size: 14px;"><?php echo $role_name; ?></td>
169
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#ffffff; color: #c420fa; font-weight: 600;"><?php echo $role_key; ?></td>
170
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#ffffff;"><?php echo ( wpfval( $roles_ug, $role_key ) ) ? '<span style="color:#43a6df; font-weight: 600;">' . implode( ', ', $roles_ug[ $role_key ] ) . '</span>' : 'default'; ?></td>
171
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding-left:20px; background:#ffffff; font-weight: 600; font-size: 14px;">
172
+ <?php echo ( wpfval( $roles_users, 'avail_roles' ) && wpfval( $roles_users, 'avail_roles', $role_key ) && $roles_users['avail_roles'][ $role_key ] ) ? '<a href="' . admin_url( 'users.php?role=' . $role_key ) . '" title="' . __( 'View Users', 'wpforo' ) . '" target="_blank"><i class="far fa-user"></i> &nbsp;' . intval( $roles_users['avail_roles'][ $role_key ] ) . '</a>' : 0; ?>
173
+ </td>
174
+ <td class="column-title" style="border-bottom:1px dotted #CCCCCC; vertical-align:middle; padding:10px; background:#ffffff;">
175
+ <?php if( ! wpfval( $roles_ug, $role_key ) ): ?>
176
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) . '&wpfaction=wpforo_usergroup_save_form&role=' . $role_key ) ?>" class="add-new-h2" style="display: inline-block; vertical-align: middle; top: 0;"><?php _e( 'Add Usergroup to synch', 'wpforo' ) ?></a>
177
+ <?php elseif( count( $roles_ug[ $role_key ] ) > 1 ): ?>
178
+ <span style="color: #f61700; font-weight: 600; padding: 0 5px; display: inline-block;"><?php _e( 'Not Synched', 'wpforo' ) ?></span>
179
+ <p class="wpf-info" style="padding: 0 4px;"><?php _e( 'One User Role cannot be synched with multiple Usergroups.', 'wpforo' ) ?></p>
180
+ <?php else: ?>
181
+ <span style="color: #00a636; font-weight: 600; padding: 0 5px; display: inline-block;"><?php _e( 'Synched', 'wpforo' ) ?></span>
182
+ <?php endif; ?>
183
+ </td>
184
+ </tr>
185
+ <?php endforeach; ?>
186
+ </tbody>
187
+ </table>
188
+ <?php endif; ?>
189
+ <script type="text/javascript">
190
+ jQuery(document).ready(function ($) {
191
+ $(document).on('click', '.wpf-synch-roles', function (e) {
192
+ e.preventDefault()
193
+ if ($('.wpf-left-users').val() >= 0) {
194
+ var btn = $(this)
195
+ btn.attr('disabled', 'disabled')
196
+ $('.fas', btn).addClass('fa-pulse fa-spinner').removeClass('wpf-spinner')
197
+ wpforo_update_roles(btn)
198
+ }
199
+ })
200
+
201
+ function wpforo_update_roles (btn) {
202
+ inprocess = true
203
+ var data = btn.parents('.wpforo-ajax-form').serialize()
204
+ $.ajax({
205
+ type: 'POST',
206
+ url: ajaxurl,
207
+ data: {
208
+ wpforo_synch_roles_data: data,
209
+ action: 'wpforo_synch_roles',
210
+ checkthis: '<?php echo wp_create_nonce( "wpforo_synch_roles" ) ?>'
211
+ }
212
+ }).done(function (response) {
213
+ try {
214
+ var resp = JSON.parse(response)
215
+ $('.wpf-step').val(resp.step)
216
+ $('.wpf-start').val(resp.start)
217
+ $('.wpf-start-id').val(resp.id)
218
+ $('.wpf-left-users').val(resp.left)
219
+ if (resp.progress < 100) {
220
+ wpforo_update_roles(btn)
221
+ } else {
222
+ btn.removeAttr('disabled')
223
+ $('.fas', btn).removeClass('fa-pulse fa-spinner').addClass('wpf-spinner')
224
+ }
225
+ if (resp.progress <= 1) {
226
+ $('.wpf-progress').text(1 + '%')
227
+ } else {
228
+ if (resp.progress < 100) {
229
+ $('.wpf-progress').text(resp.progress + '%')
230
+ } else {
231
+ $('.wpf-progress').css({ 'color': '#00a636' })
232
+ $('.wpf-progress').text(resp.progress + '% <?php _e( 'Complete!', 'wpforo' ); ?>')
233
+ $('.wpf-left-users').val(0)
234
+ $('.wpf-step').val(0)
235
+ $('.wpf-start-id').val(0)
236
+ inprocess = false
237
+ window.location.replace("<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) ) ?>")
238
+ }
239
+ }
240
+ } catch (e) {
241
+ console.log(e)
242
+ }
243
+ })
244
+ }
245
+ })
246
+ </script>
247
+ <?php endif; ?>
248
+ <!-- ############################################################### Usergroup Main Form END -->
249
+ <?php elseif( $wpfaction === 'usergroup_delete_form' ) :
250
+ $groupid = intval( wpfval( $_GET, 'groupid' ) );
251
+ $users_count = WPF()->member->get_count( [ 'groupid' => $groupid ] );
252
+ ?>
253
+ <!-- ############################################################### DELETE Usergroup -->
254
+ <form method="POST">
255
+ <?php wp_nonce_field( 'wpforo-usergroup-delete' ) ?>
256
+ <input type="hidden" name="wpfaction" value="usergroup_delete">
257
+ <input type="hidden" name="usergroup[groupid]" value="<?php echo $groupid ?>">
258
+ <div class="form-wrap">
259
+ <div class="form-field form-required">
260
+ <div class="form-field">
261
+ <table>
262
+ <tr>
263
+ <td>
264
+ <label for="delete_ug" class="menu_delete" style="color: red;">
265
+ <?php _e( 'Delete Chosen Usergroup And Users', 'wpforo' ) ?>
266
+ </label>
267
+ </td>
268
+ <td>
269
+ <input id="delete_ug" type="radio" name="usergroup[delete]" value="1" onchange="mode_changer_ug(false);" <?php echo $users_count > 50 ? 'disabled' : '' ?>>
270
+ <?php if( $users_count > 50 ) : ?>
271
+ <span style="color: #a3a3a3">(<?php _e( 'This option has disabled because you have more than 50 users in this group.', 'wpforo' ) ?>)</span>
272
+ <?php endif ?>
273
+ </td>
274
+ </tr>
275
+ <tr>
276
+ <td>
277
+ <label for="marge">
278
+ <?php _e( 'Delete Chosen Usergroup And Join Users To Other Usergroup', 'wpforo' ) ?>
279
+ </label>
280
+ </td>
281
+ <td>
282
+ <input id="marge" type="radio" name="usergroup[delete]" value="0" checked onchange="mode_changer_ug(true);">
283
+ </td>
284
+ </tr>
285
+ <tr>
286
+ <td>
287
+ <select id="ug_select" name="usergroup[mergeid]" class="postform">
288
+ <?php WPF()->usergroup->show_selectbox( [], [ $groupid, 4 ] ) ?>
289
+ </select>
290
+ <p><?php _e( 'Users will be join this usergroup', 'wpforo' ) ?></p>
291
+ </td>
292
+ </tr>
293
+ </table>
294
+ </div>
295
+ <input id="ug_submit" type="submit" class="button button-primary" value="<?php _e( 'Delete', 'wpforo' ) ?>">
296
+ </div>
297
+ </div>
298
+ </form>
299
+ <!-- ############################################################### DELETE Usergroup -->
300
+ <?php elseif( $wpfaction === 'wpforo_usergroup_save_form' ) :
301
+ $group = WPF()->usergroup->fix_group( WPF()->usergroup->get_usergroup( wpfval( $_GET, 'groupid' ) ) ); ?>
302
+ <!-- ############################################################### Add / Edit Usergroup Form -->
303
+ <script type="text/javascript">
304
+ jQuery(document).ready(function ($) {
305
+ var foo = $('#wpf_usergroup_color')
306
+ var bar = $('#wpf_usergroup_colorx')
307
+ foo.on('change input propertychange', function () {
308
+ bar.val(foo.val()).trigger('change')
309
+ })
310
+ bar.on('change input propertychange', function () {
311
+ $('.wpf-color-stat-icon').remove()
312
+ var color_code = bar.val()
313
+ if (/^#[0-9A-F]{6}$/i.test(color_code)) {
314
+ foo.val(color_code)
315
+ bar.css('border-color', 'green')
316
+ bar.after('<span class="wpf-color-stat-icon dashicons dashicons-yes-alt" style="font-size: 30px; color: green;"></span>')
317
+ $('input[name="usergroup[wpfugc]"]').prop('checked', false)
318
+ setTimeout(function () {
319
+ $('.wpf-color-stat-icon.dashicons-yes-alt').remove()
320
+ }, 4000)
321
+ } else {
322
+ bar.css('border-color', 'red')
323
+ bar.after('<span class="wpf-color-stat-icon dashicons dashicons-warning" style="font-size: 30px; color: red;"></span>')
324
+ }
325
+ })
326
+ })
327
+ </script>
328
+ <div class="wpf-info-bar" style="margin-top:20px;">
329
+ <div class="form-wrap">
330
+ <form id="add_ug" method="POST">
331
+ <?php if( $group['groupid'] ) : wp_nonce_field( 'wpforo-usergroup-edit' ) ?>
332
+ <input type="hidden" name="wpfaction" value="usergroup_edit">
333
+ <?php else : wp_nonce_field( 'wpforo-usergroup-add' ) ?>
334
+ <input type="hidden" name="wpfaction" value="usergroup_add">
335
+ <?php endif; ?>
336
+ <input type="hidden" name="usergroup[groupid]" value="<?php echo $group['groupid'] ?>">
337
+ <div style="width:100%; margin-bottom:12px;">
338
+ <div style="display:block; float:left; padding-right:20px; width:30%; padding-bottom:15px;">
339
+ <div class="wpf-label-big">
340
+ <?php _e( 'Usergroup Name', 'wpforo' );
341
+ if( $group['groupid'] === 4 ) echo '<span>: ' . __( 'Guest', 'wpforo' ) . '</span>'; ?>
342
+ &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/members/usergroups-and-permissions/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a><br>
343
+ </div>
344
+ <input name="usergroup[name]" <?php echo $group['groupid'] === 4 ? 'type="hidden"' : 'type="text"'; ?> value="<?php echo esc_attr( $group['name'] ) ?>" required style="background:#FDFDFD; min-width:320px;">
345
+ <div style="display:inline-block; margin-top:10px;">
346
+ <label for="wpf_usergroup_color" style="display:inline-block;"><?php _e( 'Usergroup Color', 'wpforo' ) ?>: </label>
347
+ <input id="wpf_usergroup_color" style="display:inline-block; vertical-align:middle; width:100px;" type="color" name="usergroup[color]" onchange="" value="<?php echo $group['color'] ?>">
348
+ <input id="wpf_usergroup_colorx" style="display:inline-block; vertical-align:middle; width:100px; padding:1px 5px;" type="text" name="usergroup[colorx]" maxlength="7" value="<?php echo $group['color'] ?>">
349
+ <label style="text-align:right;"><?php _e( 'use default link color', 'wpforo' ); ?>
350
+ <input type="checkbox" name="usergroup[wpfugc]" value="default" <?php echo ! $group['color'] ? 'checked="checked"' : '' ?>>
351
+ </label>
352
+ </div>
353
+ </div>
354
+ <?php if( $group['groupid'] === 4 ) : ?>
355
+ <input type="hidden" name="usergroup[role]" value="">
356
+ <?php elseif( $group['groupid'] === 1 ) : ?>
357
+ <input type="hidden" name="usergroup[role]" value="administrator">
358
+ <?php else: ?>
359
+ <div style="display:block; float:left; width:20%; padding-bottom:15px;">
360
+ <div class="wpf-label-big"><?php _e( 'User Role', 'wpforo' ) ?></div>
361
+ <select name="usergroup[role]" style="background:#FDFDFD; display:block;">
362
+ <?php if( $role = wpfval( $_GET, 'role' ) ) {
363
+ $selected = sanitize_title( $role );
364
+ } else {
365
+ $selected = $group['role'] ? $group['role'] : 'subscriber';
366
+ }
367
+ wp_dropdown_roles( $selected ); ?>
368
+ </select>
369
+ </div>
370
+ <?php endif; ?>
371
+ <div style="display:block; float:left; width:40%;">
372
+ <?php if( ! $group['groupid'] ): ?>
373
+ <div class="wpf-label-big"><?php _e( 'Default Forum Access', 'wpforo' ) ?></div>
374
+ <select name="usergroup[access]" style="background:#FDFDFD; display:block;">
375
+ <?php WPF()->perm->show_accesses_selectbox( 'standard' ); ?>
376
+ </select>
377
+ <div class="wpf-info" style="line-height:17px; display:block; margin-top:6px;">
378
+ <?php _e( 'This is only used when a new Usergroup is created, it automatically gets the selected Forum Access in all forums.', 'wpforo' ) ?>
379
+ </div>
380
+ <?php endif; ?>
381
+ <div style="padding: 10px 1px 5px;">
382
+ <label>
383
+ <input type="checkbox" name="usergroup[visible]" value="1" <?php echo $group['visible'] ? 'checked' : '' ?>>
384
+ <?php _e( 'Display on Members List', 'wpforo' ) ?>
385
+ </label>
386
+ <?php if( in_array( $group['groupid'], [ 1, 4 ] ) ): ?>
387
+ <input type="hidden" name="usergroup[secondary]" value="0">
388
+ <?php else: ?>
389
+ <label>
390
+ <input type="checkbox" name="usergroup[secondary]" value="1" <?php echo $group['secondary'] ? 'checked' : '' ?>>
391
+ <?php _e( 'Can be also used as Secondary Usergroup', 'wpforo' ) ?>
392
+ </label>
393
+ <?php endif; ?>
394
+ </div>
395
+ </div>
396
+ <div style="clear:both;"></div>
397
+ </div>
398
+ <?php $n = 0;
399
+ foreach( WPF()->usergroup->cans as $can => $name ) : ?>
400
+ <?php if( ! ( $n % 4 ) ): ?>
401
+ </table>
402
+ <table class="wpf-table-box-left" style="margin-right:15px; margin-bottom:15px; min-width:320px;">
403
+ <?php endif; ?>
404
+ <tr>
405
+ <th class="wpf-dw-td-nowrap">
406
+ <label class="wpf-td-label" for="wpf-can-<?php echo esc_attr( $can ) ?>">
407
+ <?php echo esc_html( __( $name, 'wpforo' ) ) ?>
408
+ </label>
409
+ </th>
410
+ <td class="wpf-dw-td-value">
411
+ <input id="wpf-can-<?php echo esc_attr( $can ) ?>" type="checkbox" value="1" name="usergroup[cans][<?php echo esc_attr( $can ) ?>]" <?php echo $group['cans'][ $can ] ? 'checked' : '' ?>>
412
+ </td>
413
+ </tr>
414
+ <?php $n ++;
415
+ endforeach; ?>
416
+ </table>
417
+ <div class="clear"></div>
418
+ <input type="submit" class="button button-primary" value="<?php _e( 'save', 'wpforo' ); ?>">
419
+ </form>
420
+ </div>
421
+ </div>
422
+ <!-- ############################################################### END of Add / Edit Usergroup -->
423
+ <?php endif; ?>
admin/settings/akismet.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="akismet_settings_save">
4
+
5
+ <?php WPF()->settings->header( 'akismet' ); ?>
6
+
7
+ <div class="wpf-opt-row">
8
+ <?php if( ! class_exists( 'Akismet' ) ): ?>
9
+ <div style="width:94%; clear:both; margin:0 0 15px 0; text-align:center; line-height:22px; font-size:14px; color:#D35206; border:1px dotted #ccc; padding:10px 20px 10px 20px;; background:#F7F5F5;">
10
+ <a href="https://wordpress.org/plugins/akismet/" target="_blank">Akismet</a> <?php _e( 'is not installed! For an advanced Spam Control please install Akismet antispam plugin, it works well with wpForo Spam Control system. Akismet is already integrated with wpForo. It\'ll help to filter posts and protect forum against spam attacks.', 'wpforo' ); ?>
11
+ </div>
12
+ <?php else: ?>
13
+ <div style="color:#fff; background:#7C9B2E; font-size:20px; padding:10px 10px; text-align:center;">
14
+ <strong>A&middot;kis&middot;met</strong> <?php _e( 'is enabled', 'wpforo' ); ?>
15
+ </div>
16
+ <?php endif; ?>
17
+ </div>
18
+
19
+ <?php WPF()->settings->form_field( 'akismet', 'akismet' ); ?>
admin/settings/antispam.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit(); ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="antispam_settings_save">
4
+
5
+ <?php WPF()->settings->header( 'antispam' ); ?>
6
+
7
+ <div class="wpf-subtitle">
8
+ <span class="dashicons dashicons-shield"></span> <?php _e( 'wpForo Spam Control', 'wpforo' ) ?>
9
+ <p class="wpf-desc"></p>
10
+ </div>
11
+
12
+ <?php WPF()->settings->form_field( 'antispam', 'spam_filter' ); ?>
13
+ <?php WPF()->settings->form_field( 'antispam', 'spam_user_ban' ); ?>
14
+ <?php WPF()->settings->form_field( 'antispam', 'spam_filter_level_topic' ); ?>
15
+ <?php WPF()->settings->form_field( 'antispam', 'spam_filter_level_post' ); ?>
16
+
17
+ <div class="wpf-subtitle">
18
+ <span class="dashicons dashicons-admin-users"></span> <?php _e( 'New Registered User', 'wpforo' ) ?>
19
+ <p class="wpf-desc"><?php _e( 'Some useful options to limit just registered users and minimize spam. These options don\'t affect users whose Usergroup has "Can edit member" and "Can pass moderation" permissions.', 'wpforo' ) ?></p>
20
+ </div>
21
+ <?php WPF()->settings->form_field( 'antispam', 'new_user_max_posts' ); ?>
22
+ <?php WPF()->settings->form_field( 'antispam', 'unapprove_post_if_user_is_new' ); ?>
23
+ <?php WPF()->settings->form_field( 'antispam', 'min_number_posts_to_edit_account' ); ?>
24
+ <?php WPF()->settings->form_field( 'antispam', 'min_number_posts_to_attach' ); ?>
25
+ <?php WPF()->settings->form_field( 'antispam', 'min_number_posts_to_link' ); ?>
26
+ <?php WPF()->settings->form_field( 'antispam', 'limited_file_ext' ); ?>
27
+
28
+ <div class="wpf-subtitle">
29
+ <span class="dashicons dashicons-paperclip"></span> <?php _e( 'Possible Spam Attachments', 'wpforo' ) ?>
30
+ <p class="wpf-desc"><?php _e( 'This tool is designed to find attachment which have been uploaded by spammers. The tool checks most common spammer filenames and suggest to delete but you should check one by one and make sure those are spam files before deleting.', 'wpforo' ) ?></p>
31
+ </div>
32
+ <?php WPF()->settings->form_field( 'antispam', 'spam_file_scanner' ); ?>
33
+ <?php WPF()->settings->form_field( 'antispam', 'exclude_file_ext' ); ?>
34
+ <div class="wpf-spam-attach">
35
+ <div class="wpf-spam-attach-dir"><?php _e( 'Directory', 'wpforo' ); ?>: <?php echo WPF()->folders['default_attachments']['dir'] ?>&nbsp;</div>
36
+ <div style="margin-top:10px; clear:both;">
37
+ <table width="100%" border="0" cellspacing="0" cellpadding="0">
38
+ <tbody>
39
+ <?php
40
+ $default_attachments_dir = WPF()->folders['default_attachments']['dir'];
41
+ if( is_dir( $default_attachments_dir ) && wpforo_setting( 'antispam', 'spam_file_scanner' ) ):
42
+ if( $handle = opendir( $default_attachments_dir ) ):
43
+ while( false !== ( $filename = readdir( $handle ) ) ):
44
+ if( $filename == '.' || $filename == '..' ) continue;
45
+
46
+ $level = 0;
47
+ $color = '';
48
+ $file = $default_attachments_dir . DIRECTORY_SEPARATOR . $filename;
49
+ $extension = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
50
+ if( ! $level = WPF()->moderation->spam_file( $filename ) ) continue;
51
+ if( $level == 2 ) $color = 'style="color:#EE9900;"';
52
+ if( $level == 3 ) $color = 'style="color:#FF0000;"';
53
+ if( $level == 4 ) $color = 'style="color:#BB0000;"';
54
+ ?>
55
+ <tr>
56
+ <td class="wpf-spam-item" <?php echo $color; ?> title="<?php echo WPF()->folders['default_attachments']['url'] . '/' . $filename ?>">
57
+ <?php if( WPF()->moderation->spam_file( $filename, 'file-open' ) ): ?>
58
+ <a href="<?php echo WPF()->folders['default_attachments']['url'] . '/' . $filename ?>" target="_blank" <?php echo $color ?>><?php echo wpforo_text( $filename, 50, false ); ?></a>
59
+ <?php else: ?>
60
+ <?php echo $filename; ?>
61
+ <?php endif; ?>
62
+ <?php echo ' (' . strtoupper( $extension ) . ' | ' . wpforo_human_filesize( filesize( $file ), 1 ) . ')'; ?>
63
+ </td>
64
+ <td class="wpf-actions"><a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam&wpfaction=delete_spam_file&sfname=' . urlencode( $filename ) ), 'wpforo_tools_antispam_files' ); ?>" title="<?php _e( 'Delete this file', 'wpforo' ); ?>" onclick="return confirm('<?php _e( 'Are you sure you want to permanently delete this file?', 'wpforo' ); ?>');"><?php _e( 'Delete', 'wpforo' ); ?></a></td>
65
+ </tr>
66
+ <?php
67
+ endwhile;
68
+ closedir( $handle );
69
+ endif;
70
+ endif;
71
+ ?>
72
+ <tr style="background:#fff;">
73
+ <td colspan="2" class="wpf-actions" style="padding-top:20px; text-align:right;">
74
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam&wpfaction=delete_all_spam_files&level=1' ), 'wpforo_tools_antispam_files' ); ?>"
75
+ title="<?php _e( 'Click to delete Blue marked files', 'wpforo' ); ?>"
76
+ onclick="return confirm('<?php _e( 'Are you sure you want to delete all BLUE marked files listed here. Please download Wordpress /wp-content/uploads/wpforo/ folder to your local computer before deleting files, this is not undoable.', 'wpforo' ); ?>');">
77
+ <?php _e( 'Delete All', 'wpforo' ); ?>
78
+ </a> |
79
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam&wpfaction=delete_all_spam_files&level=2' ), 'wpforo_tools_antispam_files' ); ?>"
80
+ title="<?php _e( 'Click to delete Orange marked files', 'wpforo' ); ?>"
81
+ style="color:#EE9900;"
82
+ onclick="return confirm('<?php _e( 'Are you sure you want to delete all ORANGE marked files listed here. Please download Wordpress /wp-content/uploads/wpforo/ folder to your local computer before deleting files, this is not undoable.', 'wpforo' ); ?>');">
83
+ <?php _e( 'Delete All', 'wpforo' ); ?>
84
+ </a> |
85
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam&wpfaction=delete_all_spam_files&level=3' ), 'wpforo_tools_antispam_files' ); ?>"
86
+ title="<?php _e( 'Click to delete Red marked files', 'wpforo' ); ?>"
87
+ style="color:#FF0000;"
88
+ onclick="return confirm('<?php _e( 'Are you sure you want to delete all RED marked files listed here. Please download Wordpress /wp-content/uploads/wpforo/ folder to your local computer before deleting files, this is not undoable.', 'wpforo' ); ?>');">
89
+ <?php _e( 'Delete All', 'wpforo' ); ?>
90
+ </a> |
91
+ <a href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam&wpfaction=delete_all_spam_files&level=4' ), 'wpforo_tools_antispam_files' ); ?>"
92
+ title="<?php _e( 'Click to delete Dark Red marked files', 'wpforo' ); ?>"
93
+ style="color:#BB0000;"
94
+ onclick="return confirm('<?php _e( 'Are you sure you want to delete all DARK RED marked files listed here. Please download Wordpress /wp-content/uploads/wpforo/ folder to your local computer before deleting files, this is not undoable.', 'wpforo' ); ?>');">
95
+ <?php _e( 'Delete All', 'wpforo' ); ?>
96
+ </a>
97
+ </td>
98
+ </tr>
99
+ </tbody>
100
+ </table>
101
+ </div>
102
+ </div>
admin/settings/authorization.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit(); ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="authorization_settings_save">
4
+
5
+ <?php WPF()->settings->header( 'authorization' ); ?>
6
+
7
+ <?php
8
+ WPF()->settings->form_field( 'authorization', 'user_register' );
9
+ WPF()->settings->form_field( 'authorization', 'user_register_email_confirm' );
10
+ WPF()->settings->form_field( 'authorization', 'manually_approval' );
11
+ WPF()->settings->form_field( 'authorization', 'manually_approval_contact_form' );
12
+ WPF()->settings->form_field( 'authorization', 'role_synch' );
13
+ WPF()->settings->form_field( 'authorization', 'user_delete_method' );
14
+ WPF()->settings->form_field( 'authorization', 'use_our_register_url' );
15
+ WPF()->settings->form_field( 'authorization', 'use_our_login_url' );
16
+ WPF()->settings->form_field( 'authorization', 'use_our_lostpassword_url' );
17
+ WPF()->settings->form_field( 'authorization', 'custom_auth_urls' );
18
+ WPF()->settings->form_field( 'authorization', 'custom_redirect_urls' );
19
+ ?>
20
+
21
+ <div class="wpf-subtitle">
22
+ <span class="dashicons dashicons-facebook"></span> <?php _e( 'Facebook API', 'wpforo' ) ?>
23
+ </div>
24
+
25
+ <?php
26
+ WPF()->settings->form_field( 'authorization', 'fb_api_config' );
27
+ WPF()->settings->form_field( 'authorization', 'fb_login' );
28
+ WPF()->settings->form_field( 'authorization', 'fb_lb_on_lp' );
29
+ WPF()->settings->form_field( 'authorization', 'fb_lb_on_rp' );
30
+ WPF()->settings->form_field( 'authorization', 'fb_redirect' );
31
+ WPF()->settings->form_field( 'authorization', 'fb_redirect_url' );
admin/settings/board.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction[]" value="slugs_settings_save">
4
+ <input type="hidden" name="wpfaction[]" value="board_settings_save">
5
+
6
+ <?php
7
+ WPF()->settings->header( 'board' );
8
+ WPF()->settings->form_field('board', 'under_construction');
9
+ WPF()->settings->form_field('board', 'cache');
10
+ ?>
11
+
12
+ <div class="wpf-subtitle">
13
+ <span class="dashicons dashicons-admin-links"></span> <?php _e( 'Permalinks', 'wpforo' ) ?>
14
+ <div class="wpf-opt-doc" style="float: right; font-size: 16px; margin-right: -8px;">
15
+ <a href="https://wpforo.com/docs/wpforo-v2/settings/board-settings/#permalinks" title="<?php _e('Read the documentation', 'wpforo') ?>" target="_blank"><i class="far fa-question-circle"></i></a>
16
+ </div>
17
+ </div>
18
+
19
+ <?php
20
+ foreach( WPF()->settings->slugs as $_slug => $slug ){
21
+ if( ! wpforo_is_slug_base( $_slug ) ) {
22
+ printf(
23
+ '<div class="wpf-opt-row">
24
+ <div class="wpf-opt-name">
25
+ <label for="slug_%1$s"> &nbsp; /%1$s</label>
26
+ </div>
27
+ <div class="wpf-opt-input">
28
+ <input id="slug_%1$s" type="text" value="%2$s" name="slugs[%1$s]" placeholder="%1$s">
29
+ </div>
30
+ <div class="wpf-opt-doc">
31
+ &nbsp;
32
+ </div>
33
+ </div>',
34
+ $_slug,
35
+ urldecode( $slug )
36
+ );
37
+ }
38
+ }
admin/settings/buddypress.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="buddypress_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'buddypress' );
7
+ WPF()->settings->form_field( 'buddypress', 'activity' );
8
+ WPF()->settings->form_field( 'buddypress', 'notification' );
9
+ WPF()->settings->form_field( 'buddypress', 'forum_tab' );
admin/settings/components.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="components_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'components' );
7
+ WPF()->settings->form_field( 'components', 'admin_cp' );
8
+ WPF()->settings->form_field( 'components', 'page_title' );
9
+ WPF()->settings->form_field( 'components', 'top_bar' );
10
+ WPF()->settings->form_field( 'components', 'top_bar_search' );
11
+ WPF()->settings->form_field( 'components', 'breadcrumb' );
12
+ WPF()->settings->form_field( 'components', 'footer' );
13
+ WPF()->settings->form_field( 'components', 'footer_stat' );
14
+ WPF()->settings->form_field( 'components', 'copyright' );
15
+
16
+
17
+
18
+
admin/settings/email.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="email_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'email' );
7
+ WPF()->settings->form_field( 'email', 'from_name' );
8
+ WPF()->settings->form_field( 'email', 'from_email' );
9
+ WPF()->settings->form_field( 'email', 'admin_emails' );
10
+ WPF()->settings->form_field( 'email', 'new_topic_notify' );
11
+ WPF()->settings->form_field( 'email', 'new_reply_notify' );
12
+ ?>
13
+
14
+ <div class="wpf-subtitle">
15
+ <span class="dashicons dashicons-admin-users"></span> <?php _e( 'Post Reporting Emails', 'wpforo' ) ?>
16
+ <p class="wpf-desc" style="padding: 5px 2px 0;"><?php _e( 'This message comes from post reporting pop-up form.', 'wpforo' ) ?></p>
17
+ </div>
18
+ <?php
19
+ WPF()->settings->form_field( 'email', 'report_email_subject' );
20
+ WPF()->settings->form_field( 'email', 'report_email_message' );
21
+ ?>
22
+
23
+
24
+ <div class="wpf-subtitle">
25
+ <span class="dashicons dashicons-admin-users"></span> <?php _e( 'New User Registration Email for Administrators', 'wpforo' ) ?>
26
+ <p class="wpf-desc" style="padding: 5px 2px 0;"><?php _e( 'This message comes when new user registers to site', 'wpforo' ) ?></p>
27
+ </div>
28
+ <?php
29
+ WPF()->settings->form_field( 'email', 'disable_new_user_admin_notification' );
30
+ WPF()->settings->form_field( 'email', 'overwrite_new_user_notification_admin' );
31
+ WPF()->settings->form_field( 'email', 'wp_new_user_notification_email_admin_subject' );
32
+ WPF()->settings->form_field( 'email', 'wp_new_user_notification_email_admin_message' );
33
+ ?>
34
+
35
+ <div class="wpf-subtitle">
36
+ <span class="dashicons dashicons-admin-users"></span> <?php _e( 'New User Registration Email for User', 'wpforo' ) ?>
37
+ <p class="wpf-desc" style="padding: 5px 2px 0;"><?php _e( 'This message comes when new user registers to site', 'wpforo' ) ?></p>
38
+ </div>
39
+ <?php
40
+ WPF()->settings->form_field( 'email', 'overwrite_new_user_notification' );
41
+ WPF()->settings->form_field( 'email', 'wp_new_user_notification_email_subject' );
42
+ WPF()->settings->form_field( 'email', 'wp_new_user_notification_email_message' );
43
+ ?>
44
+
45
+ <div class="wpf-subtitle">
46
+ <span class="dashicons dashicons-admin-users"></span> <?php _e( 'Reset Password Emails', 'wpforo' ) ?>
47
+ <p class="wpf-desc" style="padding: 5px 2px 0;"><?php _e( 'This message comes from Reset Password form.', 'wpforo' ) ?></p>
48
+ </div>
49
+ <?php
50
+ WPF()->settings->form_field( 'email', 'overwrite_reset_password_email' );
51
+ WPF()->settings->form_field( 'email', 'reset_password_email_message' );
admin/settings/forums.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="forums_settings_save">
4
+
5
+ <?php WPF()->settings->header( 'forums' ); ?>
6
+
7
+ <div class="wpf-subtitle">
8
+ <span class="dashicons dashicons-screenoptions"></span> <?php _e( 'Extended Forum Layout', 'wpforo' ) ?>
9
+ </div>
10
+
11
+ <?php
12
+ WPF()->settings->form_field( 'forums', 'layout_extended_intro_topics_toggle' );
13
+ WPF()->settings->form_field( 'forums', 'layout_extended_intro_topics_count' );
14
+ WPF()->settings->form_field( 'forums', 'layout_extended_intro_topics_length' );
15
+ ?>
16
+
17
+ <div class="wpf-subtitle">
18
+ <span class="dashicons dashicons-screenoptions"></span> <?php _e( 'Simplified Forum Layout', 'wpforo' ) ?>
19
+ </div>
20
+ <?php
21
+ WPF()->settings->form_field( 'forums', 'layout_simplified_add_topic_button' );
22
+ ?>
23
+ <div class="wpf-subtitle">
24
+ <span class="dashicons dashicons-screenoptions"></span> <?php _e( 'Q&A Forum Layout', 'wpforo' ) ?>
25
+ </div>
26
+
27
+ <?php
28
+ WPF()->settings->form_field( 'forums', 'layout_qa_intro_topics_toggle' );
29
+ WPF()->settings->form_field( 'forums', 'layout_qa_intro_topics_count' );
30
+ WPF()->settings->form_field( 'forums', 'layout_qa_intro_topics_length' );
31
+ WPF()->settings->form_field( 'forums', 'layout_qa_add_topic_button' );
32
+ ?>
33
+
34
+ <div class="wpf-subtitle">
35
+ <span class="dashicons dashicons-screenoptions"></span> <?php _e( 'Threaded Forum Layout', 'wpforo' ) ?>
36
+ </div>
37
+ <?php
38
+ WPF()->settings->form_field( 'forums', 'layout_threaded_intro_topics_toggle' );
39
+ WPF()->settings->form_field( 'forums', 'layout_threaded_display_subforums' );
40
+ //WPF()->settings->form_field( 'forums', 'layout_threaded_filter_buttons' );
41
+ WPF()->settings->form_field( 'forums', 'layout_threaded_intro_topics_count' );
42
+ WPF()->settings->form_field( 'forums', 'layout_threaded_intro_topics_length' );
43
+ WPF()->settings->form_field( 'forums', 'layout_threaded_add_topic_button' );
admin/settings/general.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction[]" value="base_slugs_settings_save">
4
+ <input type="hidden" name="wpfaction[]" value="general_settings_save">
5
+
6
+ <?php
7
+ WPF()->settings->header( 'general' );
8
+ WPF()->settings->form_field( 'general', 'admin_bar' );
9
+ WPF()->settings->form_field( 'general', 'wp_date_format' );
10
+ ?>
11
+
12
+ <div class="wpf-subtitle">
13
+ <span class="dashicons dashicons-admin-generic"></span> <?php _e( 'Miscellaneous', 'wpforo' ) ?>
14
+ </div>
15
+
16
+ <?php
17
+ WPF()->settings->form_field( 'general', 'fontawesome' );
18
+ WPF()->settings->form_field( 'general', 'debug_mode' );
19
+ ?>
20
+
21
+ <div class="wpf-subtitle">
22
+ <span class="dashicons dashicons-admin-links"></span> <?php _e( 'Permalinks', 'wpforo' ) ?>
23
+ <div class="wpf-opt-doc" style="float: right; font-size: 16px; margin-right: -8px;">
24
+ <a href="https://wpforo.com/docs/wpforo-v2/settings/general-settings/#permalinks" title="<?php _e('Read the documentation', 'wpforo') ?>" target="_blank"><i class="far fa-question-circle"></i></a>
25
+ </div>
26
+ </div>
27
+
28
+ <?php
29
+ foreach( WPF()->settings->slugs as $_slug => $slug ) {
30
+ if( wpforo_is_slug_base( $_slug ) ) {
31
+ printf(
32
+ '<div class="wpf-opt-row">
33
+ <div class="wpf-opt-name">
34
+ <label for="slug_%1$s"> &nbsp; /%1$s</label>
35
+ </div>
36
+ <div class="wpf-opt-input">
37
+ <input id="slug_%1$s" type="text" value="%2$s" name="slugs[%1$s]" placeholder="%1$s">
38
+ </div>
39
+ <div class="wpf-opt-doc">
40
+ &nbsp;
41
+ </div>
42
+ </div>',
43
+ $_slug,
44
+ urldecode( $slug )
45
+ );
46
+ }
47
+ }
admin/settings/legal.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="legal_settings_save">
4
+
5
+ <div class="wpf-opt-row">
6
+ <div class="wpf-opt-intro">
7
+ <svg xmlns="http://www.w3.org/2000/svg" height="60px" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 498 512.17"><path d="M232.21 0c86.9 55.08 165.4 81.14 232.78 74.98 3.67 74.22-2.36 138.96-17.12 194.7-10-4.08-20.68-6.52-31.7-7 11.57-46.07 16.23-99.25 13.23-159.92-57.04 5.22-123.5-16.84-197.06-63.48C168.68 88.73 103 103.21 36.04 99.7c-2.97 113.09 16.9 198.24 55.29 260.18 28.38-23.73 76.71-20.15 99.6-51.62 1.65-2.43 2.41-3.74 2.39-4.81-.01-.56-24.83-31-27.06-34.55-5.85-9.3-16.8-21.93-16.8-32.82 0-6.15 4.85-14.17 11.8-15.96-.54-9.22-.91-18.57-.91-27.84 0-5.47.1-11.01.3-16.43.3-3.44.94-4.95 1.85-8.27a58.537 58.537 0 0 1 26.13-33.18c4.43-2.8 9.25-4.98 14.19-6.77 8.96-3.27 4.62-17.43 14.46-17.65 22.99-.59 60.81 19.51 75.54 35.48 9.39 10.38 14.75 21.92 15.07 35.92l-.93 40.27c4.08 1 8.66 4.19 9.66 8.28 3.15 12.71-10.04 28.53-16.18 38.64-5.65 9.33-27.26 34.79-27.28 35-.1 1.09.46 2.47 1.94 4.69 10.53 14.48 26.44 21.54 43.3 27.25-1.87 6.71-3.07 13.64-3.53 20.74-1.76 1.23-3.4 2.6-4.91 4.11l-.1.1c-6.73 6.75-10.93 16.04-10.93 26.26v93.19c-20.32 12.65-42.28 23.4-65.81 32.26C82.71 457.27-6.26 322.77.34 71.37 79.43 75.51 157.03 58.41 232.21 0zm105.67 375.54h3.88v-11.95c0-19.96 7.87-38.16 20.55-51.39 12.79-13.33 30.44-21.6 49.88-21.6s37.11 8.27 49.88 21.6c12.69 13.23 20.56 31.42 20.56 51.39v11.95h3.88c6.32 0 11.49 5.18 11.49 11.5v113.63c0 6.33-5.17 11.5-11.49 11.5H337.88c-6.33 0-11.49-5.17-11.49-11.5V387.04c-.01-6.32 5.16-11.5 11.49-11.5zm65.2 73.48-11.96 31.34h42.13l-11.08-31.77c7.04-3.62 11.85-10.95 11.85-19.41 0-12.06-9.77-21.82-21.84-21.82-12.05 0-21.82 9.76-21.82 21.82 0 8.8 5.21 16.38 12.72 19.84zm-39.57-73.48h97.35v-11.95c0-14.2-5.53-27.06-14.43-36.34-8.81-9.19-20.93-14.9-34.24-14.9-13.31 0-25.44 5.71-34.24 14.9-8.91 9.28-14.44 22.14-14.44 36.34v11.95z"></path></svg>
8
+ <div>
9
+ <h3 style="font-weight:600; padding:0 0 5px 0; margin:0; color:#666666; font-size: 18px;">
10
+ <?php _e( 'Forum Privacy Policy and GDPR compliant', 'wpforo' ) ?> &nbsp;|&nbsp; <a href="https://wpforo.com/docs/wpforo-v2/gdpr/" rel="noreferrer" style="text-decoration: none; font-weight: normal;" target="_blank"><?php _e( 'Documentation', 'wpforo' ); ?></a>
11
+ </h3>
12
+ <p class="wpf-info">
13
+ <?php _e(
14
+ 'The General Data Protection Regulation (GDPR) (Regulation (EU) 2016/679) is a regulation by which the European Parliament, the Council of the European Union and the European Commission intend to strengthen and unify data protection for all individuals within the European Union (EU). After four years of preparation and debate the GDPR was finally approved by the EU Parliament on 14 April 2016. Enforcement date: 25 May 2018 - at which time those organizations in non-compliance may face heavy fines. More info at',
15
+ 'wpforo'
16
+ ); ?>
17
+ <a href="https://www.eugdpr.org/key-changes.html" title="<?php _e( 'GDPR Key Changes', 'wpforo' ) ?>" target="_blank" rel="noreferrer">GDPR Portal</a>
18
+ </p>
19
+ </div>
20
+ </div>
21
+ <div class="wpf-opt-doc">&nbsp;</div>
22
+ </div>
23
+ <?php
24
+
25
+ WPF()->settings->form_field( 'legal', 'contact_page_url' );
26
+ WPF()->settings->form_field( 'legal', 'checkbox_email_password' );
27
+ WPF()->settings->form_field( 'legal', 'checkbox_terms_privacy' );
28
+ WPF()->settings->form_field( 'legal', 'page_terms' );
29
+ WPF()->settings->form_field( 'legal', 'page_privacy' );
30
+ WPF()->settings->form_field( 'legal', 'checkbox_forum_privacy' );
31
+ WPF()->settings->form_field( 'legal', 'forum_privacy_text' );
32
+ WPF()->settings->form_field( 'legal', 'checkbox_fb_login' );
33
+ WPF()->settings->form_field( 'legal', 'cookies' );
34
+ ?>
35
+
36
+ <div class="wpf-subtitle">
37
+ <span class="dashicons dashicons-admin-users"></span> <?php _e('Forum Rules', 'wpforo') ?>
38
+ </div>
39
+ <?php
40
+ WPF()->settings->form_field( 'legal', 'rules_checkbox' );
41
+ WPF()->settings->form_field( 'legal', 'rules_text' );
admin/settings/logging.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="logging_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'logging' );
7
+ WPF()->settings->form_field( 'logging', 'view_logging' );
8
+ WPF()->settings->form_field( 'logging', 'track_logging' );
9
+ WPF()->settings->form_field( 'logging', 'goto_unread' );
10
+ WPF()->settings->form_field( 'logging', 'goto_unread_button' );
11
+ WPF()->settings->form_field( 'logging', 'display_forum_current_viewers' );
12
+ WPF()->settings->form_field( 'logging', 'display_topic_current_viewers' );
13
+ WPF()->settings->form_field( 'logging', 'display_recent_viewers' );
14
+ WPF()->settings->form_field( 'logging', 'display_admin_viewers' );
admin/settings/members.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="members_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'members' );
7
+ WPF()->settings->form_field( 'members', 'search_type' );
8
+ WPF()->settings->form_field( 'members', 'hide_inactive' );
9
+ WPF()->settings->form_field( 'members', 'members_per_page' );
10
+ WPF()->settings->form_field( 'members', 'list_order' );
admin/settings/notifications.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="notifications_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'notifications' );
7
+ WPF()->settings->form_field( 'notifications', 'notifications' );
8
+ WPF()->settings->form_field( 'notifications', 'notifications_live' );
9
+ WPF()->settings->form_field( 'notifications', 'notifications_bar' );
10
+
11
+
12
+
admin/settings/posting.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit(); ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="posting_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'posting' );
7
+ WPF()->settings->form_field( 'posting', 'qa_display_answer_editor' );
8
+ WPF()->settings->form_field( 'posting', 'qa_comments_rich_editor' );
9
+ WPF()->settings->form_field( 'posting', 'threaded_reply_rich_editor' );
10
+ WPF()->settings->form_field( 'posting', 'topic_title_min_length' );
11
+ WPF()->settings->form_field( 'posting', 'topic_title_max_length' );
12
+ WPF()->settings->form_field( 'posting', 'topic_body_min_length' );
13
+ WPF()->settings->form_field( 'posting', 'topic_body_max_length' );
14
+ WPF()->settings->form_field( 'posting', 'post_body_min_length' );
15
+ WPF()->settings->form_field( 'posting', 'post_body_max_length' );
16
+ WPF()->settings->form_field( 'posting', 'comment_body_min_length' );
17
+ WPF()->settings->form_field( 'posting', 'comment_body_max_length' );
18
+ ?>
19
+
20
+ <div class="wpf-subtitle">
21
+ <span class="dashicons dashicons-edit"></span> <?php _e( 'Edit and Delete', 'wpforo' ) ?>
22
+ </div>
23
+
24
+ <?php
25
+ WPF()->settings->form_field( 'posting', 'edit_own_topic_durr' );
26
+ WPF()->settings->form_field( 'posting', 'delete_own_topic_durr' );
27
+ WPF()->settings->form_field( 'posting', 'edit_own_post_durr' );
28
+ WPF()->settings->form_field( 'posting', 'delete_own_post_durr' );
29
+ WPF()->settings->form_field( 'posting', 'edit_topic' );
30
+ WPF()->settings->form_field( 'posting', 'edit_post' );
31
+ WPF()->settings->form_field( 'posting', 'edit_log_display_limit' );
32
+ ?>
33
+
34
+ <?php if( wpforo_is_module_enabled( 'revisions' ) ) : ?>
35
+ <div class="wpf-subtitle">
36
+ <span class="dashicons dashicons-schedule"></span> <?php _e( 'Post Preview and Auto Drafting', 'wpforo' ) ?>
37
+ </div>
38
+
39
+ <?php
40
+ WPF()->settings->form_field( 'posting', 'is_preview_on' );
41
+ WPF()->settings->form_field( 'posting', 'is_draft_on' );
42
+ WPF()->settings->form_field( 'posting', 'auto_draft_interval' );
43
+ WPF()->settings->form_field( 'posting', 'max_drafts_per_page' );
44
+ ?>
45
+ <?php endif ?>
46
+
47
+ <div class="wpf-subtitle">
48
+ <span class="dashicons dashicons-paperclip"></span> <?php _e( 'Attachments', 'wpforo' ) ?>
49
+ </div>
50
+
51
+ <?php
52
+ WPF()->settings->form_field( 'posting', 'max_upload_size' );
53
+ WPF()->settings->form_field( 'posting', 'attachs_to_medialib' );
54
+ ?>
55
+
56
+ <div class="wpf-subtitle">
57
+ <span class="dashicons dashicons-editor-kitchensink"></span> <?php _e( 'Rich Editor Toolbar Location', 'wpforo' ) ?>
58
+ </div>
59
+
60
+ <?php
61
+ WPF()->settings->form_field( 'posting', 'topic_editor_toolbar_location' );
62
+ WPF()->settings->form_field( 'posting', 'reply_editor_toolbar_location' );
63
+ ?>
64
+
65
+ <div class="wpf-subtitle">
66
+ <span class="dashicons dashicons-shortcode"></span> <?php _e( 'Apply Shortcodes', 'wpforo' ) ?>
67
+ </div>
68
+
69
+ <?php WPF()->settings->form_field( 'posting', 'content_do_shortcode' ); ?>
70
+
71
+ <div class="wpf-subtitle">
72
+ <span class="dashicons dashicons-text"></span> <?php _e( 'Post Content', 'wpforo' ) ?>
73
+ </div>
74
+
75
+ <?php WPF()->settings->form_field( 'posting', 'extra_html_tags' );
admin/settings/profiles.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit(); ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="profiles_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'profiles' );
7
+ WPF()->settings->form_field( 'profiles', 'profile' );
8
+ WPF()->settings->form_field( 'profiles', 'profile_header' );
9
+ WPF()->settings->form_field( 'profiles', 'profile_footer' );
10
+ WPF()->settings->form_field( 'profiles', 'url_structure' );
11
+ WPF()->settings->form_field( 'profiles', 'online_status_timeout' );
12
+ WPF()->settings->form_field( 'profiles', 'custom_title_is_on' );
13
+ WPF()->settings->form_field( 'profiles', 'default_title' );
14
+ WPF()->settings->form_field( 'profiles', 'title_groupids' );
15
+ WPF()->settings->form_field( 'profiles', 'title_secondary_groupids' );
16
+ WPF()->settings->form_field( 'profiles', 'mention_nicknames' );
17
+ WPF()->settings->form_field( 'profiles', 'default_cover' );
18
+ WPF()->settings->form_field( 'profiles', 'avatars' );
19
+ WPF()->settings->form_field( 'profiles', 'custom_avatars' );
20
+ WPF()->settings->form_field( 'profiles', 'replace_avatar' );
21
+ WPF()->settings->form_field( 'profiles', 'signature' );
admin/settings/rating.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="rating_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'rating' );
7
+ WPF()->settings->form_field( 'rating', 'rating' );
8
+ WPF()->settings->form_field( 'rating', 'rating_title' );
9
+ ?>
10
+
11
+ <div class="wpf-subtitle">
12
+ <span class="dashicons dashicons-star-filled"></span> <?php _e( 'Member Reputation and Titles', 'wpforo' ) ?>
13
+ </div>
14
+
15
+ <?php WPF()->settings->form_field( 'rating', 'levels' ); ?>
16
+
17
+ <div class="wpf-subtitle">
18
+ <span class="dashicons dashicons-calculator"></span> <?php _e( 'Reputation Point Counting', 'wpforo' ) ?>
19
+ </div>
20
+
21
+ <?php
22
+ WPF()->settings->form_field( 'rating', 'topic_points' );
23
+ WPF()->settings->form_field( 'rating', 'post_points' );
24
+ WPF()->settings->form_field( 'rating', 'like_points' );
25
+ WPF()->settings->form_field( 'rating', 'dislike_points' );
26
+ ?>
27
+
28
+ <div class="wpf-subtitle">
29
+ <span class="dashicons dashicons-awards"></span> <?php _e( 'Display Reputation Title and Badges', 'wpforo' ) ?>
30
+ </div>
31
+
32
+ <?php
33
+ WPF()->settings->form_field( 'rating', 'rating_title_ug' );
34
+ WPF()->settings->form_field( 'rating', 'rating_badge_ug' );
admin/settings/recaptcha.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="recaptcha_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'recaptcha' );
7
+ WPF()->settings->form_field( 'recaptcha', 'site_key_secret_key' );
8
+ WPF()->settings->form_field( 'recaptcha', 'theme' );
9
+ WPF()->settings->form_field( 'recaptcha', 'topic_editor' );
10
+ WPF()->settings->form_field( 'recaptcha', 'post_editor' );
11
+ WPF()->settings->form_field( 'recaptcha', 'wpf_login_form' );
12
+ WPF()->settings->form_field( 'recaptcha', 'wpf_reg_form' );
13
+ WPF()->settings->form_field( 'recaptcha', 'wpf_lostpass_form' );
14
+ WPF()->settings->form_field( 'recaptcha', 'login_form' );
15
+ WPF()->settings->form_field( 'recaptcha', 'reg_form' );
16
+ WPF()->settings->form_field( 'recaptcha', 'lostpass_form' );
admin/settings/rss.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="rss_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'rss' );
7
+ WPF()->settings->form_field( 'rss', 'feed' );
8
+ WPF()->settings->form_field( 'rss', 'feed_general' );
9
+ WPF()->settings->form_field( 'rss', 'feed_forum' );
10
+ WPF()->settings->form_field( 'rss', 'feed_topic' );
admin/settings/seo.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit(); ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="seo_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'seo' );
7
+ WPF()->settings->form_field( 'seo', 'seo_title' );
8
+ WPF()->settings->form_field( 'seo', 'seo_meta' );
9
+ WPF()->settings->form_field( 'seo', 'seo_profile' );
10
+ WPF()->settings->form_field( 'seo', 'forums_sitemap' );
11
+ WPF()->settings->form_field( 'seo', 'topics_sitemap' );
12
+ WPF()->settings->form_field( 'seo', 'members_sitemap' );
13
+ WPF()->settings->form_field( 'seo', 'dofollow' );
14
+ WPF()->settings->form_field( 'seo', 'noindex' );
admin/settings/social.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit();
2
+ $options = WPF()->settings->info->core['social']['options'];
3
+ ?>
4
+
5
+ <input type="hidden" name="wpfaction" value="social_settings_save">
6
+
7
+ <?php WPF()->settings->header( 'social' ); ?>
8
+
9
+ <div class="wpf-subtitle">
10
+ <span class="dashicons dashicons-share"></span> <?php _e( 'Share Buttons', 'wpforo' ) ?>
11
+ </div>
12
+
13
+ <!-- Option start -->
14
+ <div class="wpf-opt-row" data-wpf-opt="sb">
15
+ <div class="wpf-opt-name" style="width: 40%">
16
+ <label><?php echo esc_html( $options['sb']["label"] ) ?></label>
17
+ <p class="wpf-desc">
18
+ <?php echo esc_html( $options['sb']["description"] ) ?><br/>
19
+ </p>
20
+ </div>
21
+ <div class="wpf-opt-input" style="width: 60%; display: flex; flex-wrap: wrap;">
22
+ <div style="width: 15%; text-align: center; background: #3B5A9A; padding: 1px 18px 3px 18px; margin: 1%;">
23
+ <label for="sb_fb"><img src="<?php echo WPFORO_URL . '/assets/images/sn/fb-m.png' ?>" alt="fb-m.png" align="middle" style="width: 30px"></label><br>
24
+ <input id="sb_fb" type="checkbox" name="social[sb][fb]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb', 'fb' ) ) ?>>
25
+ </div>
26
+ <div style="width: 15%; text-align: center; background: #00A3F5; padding: 1px 18px 3px 18px; margin: 1%;">
27
+ <label for="sb_tw"><img src="<?php echo WPFORO_URL . '/assets/images/sn/tw-m.png' ?>" alt="tw-m.png" align="middle" style="width: 30px"></label><br>
28
+ &nbsp;<input id="sb_tw" type="checkbox" name="social[sb][tw]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb', 'tw' ) ) ?>>
29
+ </div>
30
+ <div style="width: 15%; text-align: center; background: #1BD741; padding: 1px 18px 3px 18px; margin: 1%;">
31
+ <label for="sb_wapp"><img src="<?php echo WPFORO_URL . '/assets/images/sn/wapp-m.png' ?>" alt=wapp-m.png"" align="middle" style="width: 30px"></label><br>
32
+ &nbsp;&nbsp;<input id="sb_wapp" type="checkbox" name="social[sb][wapp]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb', 'wapp' ) ) ?>>
33
+ </div>
34
+ <div style="width: 15%; text-align: center; background: #0A75B5; padding: 1px 18px 3px 18px; margin: 1%;">
35
+ <label for="sb_lin"><img src="<?php echo WPFORO_URL . '/assets/images/sn/lin-m.png' ?>" alt="lin-m.png" align="middle" style="width: 28px"></label><br>
36
+ &nbsp;&nbsp;<input id="sb_lin" type="checkbox" name="social[sb][lin]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb', 'lin' ) ) ?>>
37
+ </div>
38
+ <div style="width: 15%; text-align: center; background: #2D76A6; padding: 1px 18px 3px 18px; margin: 1%;">
39
+ <label for="sb_vk"><img src="<?php echo WPFORO_URL . '/assets/images/sn/vk-m.png' ?>" alt="vk-m.png" align="middle" style="width: 30px"></label><br>
40
+ &nbsp;&nbsp;<input id="sb_vk" type="checkbox" name="social[sb][vk]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb', 'vk' ) ) ?>>
41
+ </div>
42
+ <div style="width: 15%; text-align: center; background: #FF7800; padding: 1px 18px 3px 18px; margin: 1%;">
43
+ <label for="sb_ok"><img src="<?php echo WPFORO_URL . '/assets/images/sn/ok-m.png' ?>" alt="ok-m.png" align="middle" style="width: 30px"></label><br>
44
+ &nbsp;&nbsp;<input id="sb_ok" type="checkbox" name="social[sb][ok]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb', 'ok' ) ) ?>>
45
+ </div>
46
+ </div>
47
+ <?php echo WPF()->settings->get_doc_link( $options['sb'] ) ?>
48
+ </div>
49
+ <!-- Option end -->
50
+
51
+ <?php
52
+ WPF()->settings->form_field( 'social', 'sb_on' );
53
+ WPF()->settings->form_field( 'social', 'sb_style' );
54
+ WPF()->settings->form_field( 'social', 'sb_type' );
55
+ WPF()->settings->form_field( 'social', 'sb_toggle_on' );
56
+ ?>
57
+
58
+ <!-- Option start -->
59
+ <div class="wpf-opt-row" data-wpf-opt="sb_toggle">
60
+ <div class="wpf-opt-name">
61
+ <label><?php echo esc_html( $options['sb_toggle']["label"] ) ?></label>
62
+ <p class="wpf-desc">
63
+ <?php echo esc_html( $options['sb_toggle']["description"] ) ?>
64
+ </p>
65
+ </div>
66
+ <div class="wpf-opt-input" style="display: flex; justify-content: flex-start; flex-wrap: wrap;">
67
+ <div style="background: #fff; width: 32%; text-align: center; padding: 1px 18px 3px 18px; margin: 1%; border: 1px solid #ddd;">
68
+ <label for="sb_toggle_1"><img src="<?php echo WPFORO_URL . '/assets/images/sn/toggle-1.png' ?>" alt="toggle-1.png" align="middle"></label><br>
69
+ &nbsp;&nbsp;<input id="sb_toggle_1" type="radio" name="social[sb_toggle]" value="1" <?php checked( wpforo_setting( 'social', 'sb_toggle' ), 1 ); ?>>
70
+ </div>
71
+ <div style="background: #fff; width: 32%; text-align: center; padding: 1px 18px 3px 18px; margin: 1%; border: 1px solid #ddd;">
72
+ <label for="sb_toggle_2"><img src="<?php echo WPFORO_URL . '/assets/images/sn/toggle-2.png' ?>" align="middle" alt="toggle-2.png"></label><br>
73
+ &nbsp;&nbsp;<input id="sb_toggle_2" type="radio" name="social[sb_toggle]" value="2" <?php checked( wpforo_setting( 'social', 'sb_toggle' ), 2 ); ?>>
74
+ </div>
75
+ <div style="background: #fff; width: 32%; text-align: center; padding: 1px 18px 3px 18px; margin: 1%; border: 1px solid #ddd;">
76
+ <label for="sb_toggle_3"><img src="<?php echo WPFORO_URL . '/assets/images/sn/toggle-3.png' ?>" align="middle" alt="toggle-3.png"></label><br>
77
+ &nbsp;&nbsp;<input id="sb_toggle_3" type="radio" name="social[sb_toggle]" value="3" <?php checked( wpforo_setting( 'social', 'sb_toggle' ), 3 ); ?>>
78
+ </div>
79
+ <div style="background: #fff; width: 32%; text-align: center; padding: 1px 18px 3px 18px; margin: 1%; border: 1px solid #ddd;">
80
+ <label for="sb_toggle_4"><img src="<?php echo WPFORO_URL . '/assets/images/sn/toggle-4.png' ?>" align="middle" alt="toggle-4.png"></label><br>
81
+ &nbsp;&nbsp;<input id="sb_toggle_4" type="radio" name="social[sb_toggle]" value="4" <?php checked( wpforo_setting( 'social', 'sb_toggle' ), 4 ); ?>>
82
+ </div>
83
+ </div>
84
+ <?php echo WPF()->settings->get_doc_link( $options['sb_toggle'] ) ?>
85
+ </div>
86
+ <!-- Option end -->
87
+ <?php
88
+ WPF()->settings->form_field( 'social', 'sb_toggle_type' );
89
+ WPF()->settings->form_field( 'social', 'sb_icon' );
90
+ ?>
91
+
92
+ <table class="wpforo_settings_table" data-wpf-opt="sb_location">
93
+ <tr>
94
+ <td style="padding: 0;"></td>
95
+ </tr>
96
+ <tr>
97
+ <th style="padding: 15px 0" colspan="2">
98
+ <label style="font-size: 15px; color: #555;font-weight: 600; margin-bottom: 5px; display: block;"><?php _e( 'Share Button Locations', 'wpforo' ); ?></label>
99
+ <p class="wpf-info" style="margin-bottom: 10px;"><?php _e( 'The post sharing toggle can be displayed either on the left side or on the top of each post. The general share buttons can be displayed on both (top and bottom) locations.', 'wpforo' ); ?></p>
100
+ <div style="padding-right: 10px; display: inline-block; width: auto; border-right: 1px solid #ccc; ">
101
+ <p style="text-align: center; margin: 0; font-weight: normal; font-size: 14px;"><?php _e( 'General Share Buttons', 'wpforo' ); ?></p>
102
+ <div style="float: left; background: #fff; display: inline-block; text-align: center; padding: 1px 5px 3px 5px; margin: 10px 5px 10px 0;">
103
+ <label for="sb_location_4"><img src="<?php echo WPFORO_URL . '/assets/images/sn/location-3.png' ?>" alt="location-3.png" align="middle" style="width: 130px"></label><br>
104
+ &nbsp;&nbsp;<input id="sb_location_4" type="checkbox" name="social[sb_location][top]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb_location', 'top' ) ) ?>>
105
+ </div>
106
+ <div style="float: left; background: #fff; display: inline-block; text-align: center; padding: 1px 5px 3px 5px; margin: 10px 5px 10px 0;">
107
+ <label for="sb_location_5"><img src="<?php echo WPFORO_URL . '/assets/images/sn/location-5.png' ?>" alt="location-5.png" align="middle" style="width: 130px"></label><br>
108
+ &nbsp;&nbsp;<input id="sb_location_5" type="checkbox" name="social[sb_location][bottom]" value="1" <?php checked( (bool) wpforo_setting( 'social', 'sb_location', 'bottom' ) ) ?>>
109
+ </div>
110
+ <div style="clear: both;"></div>
111
+ </div>
112
+ <div style="padding-left: 10px; display: inline-block; width: auto;">
113
+ <p style="text-align: center; margin: 0; font-weight: normal; font-size: 14px;"><?php _e( 'Post Sharing Toggle', 'wpforo' ); ?></p>
114
+ <div style="float: left; background: #fff; display: inline-block; text-align: center; padding: 1px 5px 3px 5px; margin: 10px 5px 10px 0;">
115
+ <label for="sb_location_1"><img src="<?php echo WPFORO_URL . '/assets/images/sn/location-1.png' ?>" alt="location-1.png" align="middle" style="width: 130px"></label><br>
116
+ &nbsp;&nbsp;<input id="sb_location_1" type="radio" name="social[sb_location_toggle]" value="left" <?php checked( wpforo_setting( 'social', 'sb_location_toggle' ), 'left' ); ?>>
117
+ </div>
118
+ <div style="float: left; background: #fff; display: inline-block; text-align: center; padding: 1px 5px 3px 5px; margin: 10px 5px 10px 0;">
119
+ <label for="sb_location_3"><img src="<?php echo WPFORO_URL . '/assets/images/sn/location-6.png' ?>" align="middle" style="width: 130px" alt="location-6.png"></label><br>
120
+ &nbsp;&nbsp;<input id="sb_location_3" type="radio" name="social[sb_location_toggle]" value="right" <?php checked( wpforo_setting( 'social', 'sb_location_toggle' ), 'right' ); ?>>
121
+ </div>
122
+ <div style="float: left; background: #fff; display: inline-block; text-align: center; padding: 1px 5px 3px 5px; margin: 10px 5px 10px 0;">
123
+ <label for="sb_location_2"><img src="<?php echo WPFORO_URL . '/assets/images/sn/location-2.png' ?>" alt="location-2.png" align="middle" style="width: 130px"></label><br>
124
+ &nbsp;&nbsp;<input id="sb_location_2" type="radio" name="social[sb_location_toggle]" value="top" <?php checked( wpforo_setting( 'social', 'sb_location_toggle' ), 'top' ); ?>>
125
+ </div>
126
+ <div style="clear: both;"></div>
127
+ </div>
128
+ </th>
129
+ </tr>
130
+ </table>
admin/settings/styles.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit(); ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="styles_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'styles' );
7
+ WPF()->settings->form_field( 'styles', 'font_sizes' );
8
+ WPF()->settings->form_field( 'styles', 'custom_css' );
9
+ $colorids = apply_filters( 'wpforo_manageable_colorids', [ 1, 3, 11, 12, 14 ] );
10
+ ?>
11
+
12
+ <h3 style="margin:20px 0 0; padding:10px 0; border-bottom:3px solid #F5F5F5; font-size: 15px;" data-wpf-opt="style"><?php _e( 'Forum Styles', 'wpforo' ); ?> &nbsp;<a href="https://wpforo.com/docs/wpforo-v2/wpforo-settings/style-settings/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a> &nbsp;|&nbsp; <a href="https://wpforo.com/docs/wpforo-v2/forum-themes/theme-styles/" target="_blank"
13
+ style="font-size:13px; text-decoration:none;"><?php _e( 'Colors Documentation', 'wpforo' ); ?> &raquo;</a>
14
+ </h3>
15
+ <table style="width:95%; border:none; padding:5px; margin-left:10px; margin-top:15px;">
16
+ <tbody>
17
+ <tr class="form-field form-required">
18
+ <td class="wpf-dw-td-value-p">
19
+ <table class="wpforo-style-color-wrapper" style="margin-right:10px; width:20px;">
20
+ <tr>
21
+ <td class="wpfo-settings-style-colorid-hash">#</td>
22
+ </tr>
23
+ <?php
24
+ foreach( $colorids as $colorid ) {
25
+ printf( '<tr><td><div class="wpfo-settings-style-colorid">%1$d</div></td></tr>', $colorid );
26
+ }
27
+ ?>
28
+ </table>
29
+ <?php
30
+ foreach( wpforo_setting( 'styles', 'color_styles' ) as $color_style => $colors ): ?>
31
+ <table class="wpforo-style-color-wrapper" style="border-right:2px solid #eee; margin-right:10px; padding-left:5px; <?php echo ( $color_style === wpforo_setting( 'styles', 'color_style' ) ) ? 'background: #E8FFE5; width: 130px; text-align: center;' : 'background: transparent'; ?>">
32
+ <tr>
33
+ <td>
34
+ <div style="float: left; text-align: center; width: 27px;">
35
+ <input style="margin: 0;" <?php checked( $color_style === wpforo_setting( 'styles', 'color_style' ) ) ?> type="radio" name="styles[color_style]" value="<?php wpfo( $color_style ) ?>" id="wpforo_stle_<?php wpfo( $color_style ) ?>">
36
+ </div>
37
+ <div style="text-transform: uppercase; text-align: left; float: left; font-weight: bold; font-size: 13px; padding-bottom: 5px;"><label for="wpforo_stle_<?php wpfo( $color_style ) ?>">&nbsp;<?php _e( $color_style, 'wpforo' ); ?></label></div>
38
+ <div style="clear: both;"></div>
39
+ </td>
40
+ </tr>
41
+ <?php foreach( $colorids as $colorid ) : ?>
42
+ <tr>
43
+ <td style="border-bottom:1px solid #ddd;">
44
+ <div class="wpforo-style-field">
45
+ <?php if( $color_style === wpforo_setting( 'styles', 'color_style' ) ): ?>
46
+ <input class="wpforo-color-field" name="styles[color_styles][<?php wpfo( $color_style ) ?>][<?php wpfo( $colorid ) ?>]" type="text" value="<?php wpfo( strtoupper( $colors[ $colorid ] ) ); ?>" title="<?php wpfo( strtoupper( $colors[ $colorid ] ) ); ?>">
47
+ <?php else: ?>
48
+ <input style="width:90%; height: 23px; box-sizing: border-box; padding:0;" name="styles[color_styles][<?php wpfo( $color_style ) ?>][<?php wpfo( $colorid ); ?>]" type="color" value="<?php wpfo( strtoupper( $colors[ $colorid ] ) ); ?>" title="<?php wpfo( strtoupper( $colors[ $colorid ] ) ); ?>">
49
+ <?php endif; ?>
50
+ </div>
51
+ </td>
52
+ </tr>
53
+ <?php endforeach; ?>
54
+ </table>
55
+ <?php endforeach; ?>
56
+ <div style="clear:both;"></div>
57
+ <div class="wpf-color-desc">
58
+ <ul>
59
+ <li><strong style="text-transform: uppercase;"><?php _e( 'Color', 'wpforo' ) ?> #1</strong>
60
+ <ul>
61
+ <li><?php _e( 'Wherever you see white background and white font color', 'wpforo' ) ?></li>
62
+ </ul>
63
+ </li>
64
+ <li><strong style="text-transform: uppercase;"><?php _e( 'Color', 'wpforo' ) ?> #3</strong>
65
+ <ul>
66
+ <li><?php _e( 'Post content font color', 'wpforo' ) ?></li>
67
+ <li><?php _e( 'Forum menu bar background color', 'wpforo' ) ?></li>
68
+ <li><?php _e( 'Footer top bar background color', 'wpforo' ) ?></li>
69
+ <li><?php _e( 'Footer bottom "powered by" bar background color', 'wpforo' ) ?></li>
70
+ </ul>
71
+ </li>
72
+ <li><span style="color: #3366ff; text-transform: uppercase;"><strong><?php _e( 'Color', 'wpforo' ) ?> #11</strong></span>
73
+ <ul>
74
+ <li><?php _e( "Links' hover/active font color", 'wpforo' ) ?></li>
75
+ <li><?php _e( 'Topic/post action link hover color (reply, quote, like, sticky, closed, move, delete)', 'wpforo' ) ?></li>
76
+ </ul>
77
+ </li>
78
+ <li><span style="color: #3366ff; text-transform: uppercase;"><strong><?php _e( 'Color', 'wpforo' ) ?> #12</strong></span> - <span style="color: #ff0000;"><strong>[ PRIMARY COLOR ]</strong></span>
79
+ <ul>
80
+ <li><?php _e( 'Buttons background color', 'wpforo' ) ?></li>
81
+ <li><?php _e( 'Active menu background color', 'wpforo' ) ?></li>
82
+ <li><?php _e( 'Category panel background color', 'wpforo' ) ?></li>
83
+ <li><?php _e( 'Topic list head panel background color', 'wpforo' ) ?></li>
84
+ <li><?php _e( 'Post list head panel background color', 'wpforo' ) ?></li>
85
+ <li><?php _e( 'Top right pop-up message background color', 'wpforo' ) ?></li>
86
+ </ul>
87
+ </li>
88
+ <li><span style="color: #3366ff; text-transform: uppercase;"><strong><?php _e( 'Color', 'wpforo' ) ?> #14</strong></span>
89
+ <ul>
90
+ <li><?php _e( 'Button border color and button hover background color', 'wpforo' ) ?></li>
91
+ </ul>
92
+ </li>
93
+ </ul>
94
+ </div>
95
+ </td>
96
+ </tr>
97
+ </tbody>
98
+ </table>
99
+ <div style="clear: both;"></div>
100
+ <hr>
101
+
102
+ <?php if( $dynamic_css = WPF()->tpl->generate_dynamic_css() ) : ?>
103
+ <script type="text/javascript">
104
+ function wpforo_input_select_all_and_copy (t) {
105
+ t.select()
106
+ if (document.execCommand('copy')) {
107
+ jQuery('#dynamic-css-code-wrap').addClass('wpf_copy_animate')
108
+ setTimeout(function () {
109
+ jQuery('#dynamic-css-code-wrap').removeClass('wpf_copy_animate')
110
+ }, 1000)
111
+ }
112
+ }
113
+ </script>
114
+ <div id="dynamic-css-notice-wrap">
115
+ <div id="dynamic-css-help-steps-wrap">
116
+ <p style="font-size: 15px;">
117
+ <b><i class="fas fa-info-circle" aria-hidden="true"></i> <?php _e( 'Problems with colors?', 'wpforo' ); ?></b><br>
118
+ <?php printf( __( 'After changing and saving colors, go to the forum front-end and press %s twice. If you don\'t see any change, please follow to the instruction below.', 'wpforo' ), '<b>CTRL+F5</b>' ); ?>
119
+ </p>
120
+ <p style="font-size: 15px;"><?php _e( 'In most cases, this problem comes from your server file writing permissions. Files are not permitted to change, thus the forum color provider colors.css file is not updated with your changes. If you cannot fix this issue in hosting server, then the following easy steps can solve your problem:', 'wpforo' ) ?></p>
121
+ <ol>
122
+ <li style="font-size: 14px; margin-bottom: 1px; line-height: 1.5"><?php printf( __( 'Create colors.css file or simply download %s file with the CSS code provided in the textarea below,', 'wpforo' ), '<code>colors.css</code>' ) ?></li>
123
+ <li style="font-size: 14px; margin-bottom: 1px; line-height: 1.5"><?php printf( __( 'Upload and replace %s file in %s directory,', 'wpforo' ), '<code>colors.css</code>', '<code>' . WPF()->tpl->template_dir . DIRECTORY_SEPARATOR . '</code>' ) ?></li>
124
+ <li style="font-size: 14px; margin-bottom: 1px; line-height: 1.5"><?php printf( __( 'Delete website cache, reset CSS file optimizer and minifier plugins caches, purge CDN data (if you have), then go to the forum front-end and press %s twice.', 'wpforo' ), '<b>CTRL+F5</b>' ) ?></li>
125
+ </ol>
126
+ </div>
127
+ <div id="dynamic-css-code-wrap">
128
+ <label for="colors_css" class="dynamic-css-fname"><i class="fas fa-file-code"></i>&nbsp;colors.css</label>
129
+ <textarea id="colors_css" readonly class="dynamic-css-code" rows="10" onclick="wpforo_input_select_all_and_copy(this)"><?php echo $dynamic_css ?></textarea>
130
+ <div class="wpf_copied_txt"><span><?php _e( 'Copied', 'wpforo' ) ?></span></div>
131
+ </div>
132
+ <a style="font-size: 14px; text-decoration: none;" href="<?php echo wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) . '&tab=styles&wpfaction=colors_css_download' ), 'dynamic_css_download' ) ?>"><i class="fas fa-file-download"></i> <?php _e( 'Download', 'wpforo' ) ?> colors.css</a>
133
+ <br style="clear: both">
134
+ </div>
135
+ <?php endif; ?>
136
+
137
+ <script>jQuery(document).ready(function ($) {$(function () { $('.wpforo-color-field').wpColorPicker() })})</script>
admin/settings/subscriptions.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="subscriptions_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'subscriptions' );
7
+ WPF()->settings->form_field( 'subscriptions', 'subscribe_confirmation' );
8
+ WPF()->settings->form_field( 'subscriptions', 'subscribe_checkbox_on_post_editor' );
9
+ WPF()->settings->form_field( 'subscriptions', 'subscribe_checkbox_default_status' );
10
+ WPF()->settings->form_field( 'subscriptions', 'user_mention_notify' );
11
+ WPF()->settings->form_field( 'subscriptions', 'user_following_notify' );
12
+ ?>
13
+
14
+ <div class="wpf-subtitle">
15
+ <span class="dashicons dashicons-email-alt" style="font-size: 22px;"></span>&nbsp; <?php _e( 'Email Template - Subscription Confirmation', 'wpforo' ) ?>
16
+ </div>
17
+
18
+ <?php
19
+ WPF()->settings->form_field( 'subscriptions', 'confirmation_email_subject' );
20
+ WPF()->settings->form_field( 'subscriptions', 'confirmation_email_message' );
21
+ ?>
22
+
23
+ <div class="wpf-subtitle">
24
+ <span class="dashicons dashicons-email-alt" style="font-size: 22px;"></span>&nbsp; <?php _e( 'Email Template - New Topic Notification', 'wpforo' ) ?>
25
+ </div>
26
+
27
+ <?php
28
+ WPF()->settings->form_field( 'subscriptions', 'new_topic_notification_email_subject' );
29
+ WPF()->settings->form_field( 'subscriptions', 'new_topic_notification_email_message' );
30
+ ?>
31
+
32
+ <div class="wpf-subtitle">
33
+ <span class="dashicons dashicons-email-alt" style="font-size: 22px;"></span>&nbsp; <?php _e( 'Email Template - New Reply Notification', 'wpforo' ) ?>
34
+ </div>
35
+
36
+ <?php
37
+ WPF()->settings->form_field( 'subscriptions', 'new_post_notification_email_subject' );
38
+ WPF()->settings->form_field( 'subscriptions', 'new_post_notification_email_message' );
39
+ ?>
40
+
41
+ <div class="wpf-subtitle">
42
+ <span class="dashicons dashicons-email-alt" style="font-size: 22px;"></span>&nbsp; <?php _e( 'Email Template - User Mentioning', 'wpforo' ) ?>
43
+ </div>
44
+
45
+ <?php
46
+ WPF()->settings->form_field( 'subscriptions', 'user_mention_email_subject' );
47
+ WPF()->settings->form_field( 'subscriptions', 'user_mention_email_message' );
48
+ ?>
49
+
50
+ <div class="wpf-subtitle">
51
+ <span class="dashicons dashicons-email-alt" style="font-size: 22px;"></span>&nbsp; <?php _e( 'Email Template - User Following', 'wpforo' ) ?>
52
+ </div>
53
+
54
+ <?php
55
+ WPF()->settings->form_field( 'subscriptions', 'user_following_email_subject' );
56
+ WPF()->settings->form_field( 'subscriptions', 'user_following_email_message' );
admin/settings/tags.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="tags_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'tags' );
7
+ WPF()->settings->form_field( 'tags', 'max_per_topic' );
8
+ WPF()->settings->form_field( 'tags', 'per_page' );
9
+ WPF()->settings->form_field( 'tags', 'length' );
10
+ WPF()->settings->form_field( 'tags', 'suggest_limit' );
11
+ WPF()->settings->form_field( 'tags', 'lowercase' );
admin/settings/topics.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit(); ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="topics_settings_save">
4
+
5
+ <?php WPF()->settings->header( 'topics' ); ?>
6
+
7
+ <div class="wpf-subtitle">
8
+ <span class="dashicons dashicons-screenoptions"></span> <?php _e( 'Extended Forum Layout', 'wpforo' ) ?>
9
+ </div>
10
+
11
+ <?php
12
+ WPF()->settings->form_field( 'topics', 'layout_extended_intro_posts_toggle' );
13
+ WPF()->settings->form_field( 'topics', 'layout_extended_intro_posts_count' );
14
+ WPF()->settings->form_field( 'topics', 'layout_extended_intro_posts_length' );
15
+ ?>
16
+
17
+ <div class="wpf-subtitle">
18
+ <span class="dashicons dashicons-screenoptions"></span> <?php _e( 'Q&A Forum Layout', 'wpforo' ) ?>
19
+ </div>
20
+
21
+ <?php
22
+ WPF()->settings->form_field( 'topics', 'layout_qa_posts_per_page' );
23
+ WPF()->settings->form_field( 'topics', 'layout_qa_comments_limit_count' );
24
+ WPF()->settings->form_field( 'topics', 'layout_qa_first_post_reply' );
25
+ ?>
26
+
27
+ <div class="wpf-subtitle">
28
+ <span class="dashicons dashicons-screenoptions"></span> <?php _e( 'Threaded Forum Layout', 'wpforo' ) ?>
29
+ </div>
30
+
31
+ <?php
32
+ WPF()->settings->form_field( 'topics', 'layout_threaded_posts_per_page' );
33
+ WPF()->settings->form_field( 'topics', 'layout_threaded_nesting_level' );
34
+ WPF()->settings->form_field( 'topics', 'layout_threaded_first_post_reply' );
35
+ ?>
36
+
37
+ <div class="wpf-subtitle">
38
+ <span class="dashicons dashicons-admin-generic"></span> <?php _e( 'General', 'wpforo' ) ?>
39
+ </div>
40
+
41
+ <?php
42
+ WPF()->settings->form_field( 'topics', 'topics_per_page' );
43
+ WPF()->settings->form_field( 'topics', 'posts_per_page' );
44
+ WPF()->settings->form_field( 'topics', 'search_max_results' );
45
+ ?>
46
+
47
+ <?php $union_first_post = WPF()->settings->info->core['topics']['options']['union_first_post'] ?>
48
+ <div class="wpf-opt-row" data-wpf-opt="union_first_post">
49
+ <div class="wpf-opt-name">
50
+ <label><?php echo esc_html( $union_first_post["label"] ) ?></label>
51
+ <p class="wpf-desc"><?php echo esc_html( $union_first_post["description"] ) ?></p>
52
+ </div>
53
+ <div class="wpf-opt-input">
54
+ <table style="margin-left: -1px;">
55
+ <tbody>
56
+ <?php if( $layouts = WPF()->tpl->find_layouts() ) : ?>
57
+ <tr>
58
+ <td>
59
+ <table style="margin-left: -1px;">
60
+ <?php
61
+ foreach( $layouts as $layout ) :
62
+ $name = sprintf( 'topics[union_first_post][%1$d]', $layout['id'] );
63
+ $value = WPF()->post->get_option_union_first_post( $layout['id'] );
64
+ ?>
65
+ <tr style="background-color: transparent;">
66
+ <td style="border-bottom: 1px dashed #aaaaaa; padding: 8px 0;">
67
+ <div class="wpf-switch-field">
68
+ <input type="radio" value="1" name="<?php echo $name ?>" id="<?php echo sanitize_title( $name ) ?>_1" <?php checked( $value ) ?>><label for="<?php echo sanitize_title( $name ) ?>_1"><?php _e( 'Yes', 'wpforo' ); ?></label> &nbsp;
69
+ <input type="radio" value="0" name="<?php echo $name ?>" id="<?php echo sanitize_title( $name ) ?>_0" <?php checked( $value, false ) ?>><label for="<?php echo sanitize_title( $name ) ?>_0"><?php _e( 'No', 'wpforo' ); ?></label>
70
+ </div>
71
+ </td>
72
+ <th style="border-bottom: 1px dashed #aaaaaa; width: auto;"><label style="font-weight: normal;"><?php echo $layout['name'] ?></label></th>
73
+ </tr>
74
+ <?php endforeach; ?>
75
+ </table>
76
+ </td>
77
+ </tr>
78
+ <?php endif; ?>
79
+ </tbody>
80
+ </table>
81
+ </div>
82
+ <?php echo WPF()->settings->get_doc_link( $union_first_post ) ?>
83
+ </div>
84
+ <!-- Option end -->
85
+
86
+ <?php
87
+ WPF()->settings->form_field( 'topics', 'recent_posts_type' );
88
+ WPF()->settings->form_field( 'topics', 'topic_head' );
89
+ WPF()->settings->form_field( 'topics', 'topic_head_expanded' );
admin/settings/um.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php if( ! defined( "ABSPATH" ) ) exit() ?>
2
+
3
+ <input type="hidden" name="wpfaction" value="um_settings_save">
4
+
5
+ <?php
6
+ WPF()->settings->header( 'um' );
7
+ WPF()->settings->form_field( 'um', 'notification' );
8
+ WPF()->settings->form_field( 'um', 'forum_tab' );
admin/tools-tabs/cleanup.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! current_user_can( 'administrator' ) ) exit;
5
+
6
+ if( ! isset( $_GET['action'] ) ): ?>
7
+ <form action="" method="POST" class="validate">
8
+ <?php wp_nonce_field( 'wpforo-tools-cleanup' ) ?>
9
+ <input type="hidden" name="wpfaction" value="cleanup_options_save">
10
+ <div class="wpf-tool-box" style="width:50%;">
11
+ <h3><?php _e( 'Delete Inactive Users', 'wpforo' ); ?>
12
+ <p class="wpf-info"><?php _e( 'Inactive users are the account owners who have no topics, posts, comments and subscriptions for new content. In 99&#37; cases this kind of accounts are being registered by Spammers. This tool allows you to only keep active and known inactive users.', 'wpforo' ); ?></p>
13
+ </h3>
14
+ <div style="margin-top:10px; clear:both;">
15
+ <table style="width:100%;">
16
+ <tbody>
17
+ <tr>
18
+ <th><label><?php _e( 'Inactive users who have been registered more than', 'wpforo' ); ?></label></th>
19
+ <td><input type="number" min="0" max="100" name="wpforo_tools_cleanup[user_reg_days_ago]" value="<?php wpfo( WPF()->tools_cleanup['user_reg_days_ago'] ) ?>" class="wpf-field" style="width:70px;"> <span style="white-space:nowrap;"><?php _e( 'days ago', 'wpforo' ); ?></span></td>
20
+ </tr>
21
+ <?php $ugroups = WPF()->usergroup->usergroup_list_data(); ?>
22
+ <tr>
23
+ <td colspan="2">
24
+ <label style="display:inline-block; border-bottom:1px solid #ccc; padding:0px 50px 5px 10px; margin-bottom:5px;"> <?php _e( 'Filter by Usergroups', 'wpforo' ); ?></label><br>
25
+ <?php
26
+ foreach( $ugroups as $ugroup ) {
27
+ if( $ugroup['groupid'] == 4 ) {
28
+ continue;
29
+ }
30
+ $value = ( isset( WPF()->tools_cleanup['usergroup'][ $ugroup['groupid'] ] ) ) ? WPF()->tools_cleanup['usergroup'][ $ugroup['groupid'] ] : 0;
31
+ echo '<label style="display:inline-block; width:23%; text-align:center;"><input name="wpforo_tools_cleanup[usergroup][' . intval( $ugroup['groupid'] ) . ']" value="1" type="checkbox" ' . wpfo_check( 1, $value, 'checked', false ) . ' style="font-size: 12px; height: 24px; width: 100%;"> ' . esc_html( $ugroup['name'] ) . '</label>';
32
+ }
33
+ ?>
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <th><label><?php _e( 'Enable Auto-cleanup of inactive users', 'wpforo' ); ?></label></th>
38
+ <td>
39
+ <div class="wpf-switch-field">
40
+ <input id="auto_cleanup_users_yes" type="radio" name="wpforo_tools_cleanup[auto_cleanup_users]" value="1" <?php wpfo_check( WPF()->tools_cleanup['auto_cleanup_users'], 1 ); ?>/><label for="auto_cleanup_users_yes"><?php _e( 'Yes', 'wpforo' ); ?></label> &nbsp;
41
+ <input id="auto_cleanup_users_no" type="radio" name="wpforo_tools_cleanup[auto_cleanup_users]" value="0" <?php wpfo_check( WPF()->tools_cleanup['auto_cleanup_users'], 0 ); ?>/><label for="auto_cleanup_users_no"><?php _e( 'No', 'wpforo' ); ?></label>
42
+ </div>
43
+ </td>
44
+ </tr>
45
+ </tbody>
46
+ </table>
47
+ </div>
48
+ </div>
49
+ <div class="wpforo_settings_foot" style="clear:both; margin-top:20px;">
50
+
51
+ <input type="submit" class="button button-primary" value="<?php _e( 'Update Options', 'wpforo' ); ?>"/>
52
+ </div>
53
+ </form>
54
+ <?php
55
+ endif;
admin/tools-tabs/debug.php ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! current_user_can( 'administrator' ) ) exit;
5
+ $view = ( wpfval( $_GET, 'view' ) ) ? sanitize_text_field( $_GET['view'] ) : '';
6
+ ?>
7
+
8
+ <form method="POST" class="validate">
9
+ <div class="wpf-tool-box" style="margin-top: 15px;">
10
+ <h3>
11
+ <?php _e( 'Debug Information', 'wpforo' ); ?> &nbsp;&nbsp;&nbsp; - &nbsp;&nbsp;&nbsp;
12
+
13
+ <?php if( $view == 'user' || $view == '' ): ?>
14
+ [ <?php _e( 'User Data', 'wpforo' ); ?> ]
15
+ <?php else: ?>
16
+ [ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=debug&view=user' ) ?>" style="text-decoration: none; outline: none;box-shadow:none;"><?php _e( 'User Data', 'wpforo' ); ?></a> ]
17
+ <?php endif; ?>
18
+
19
+ <?php if( $view == 'server' ): ?>
20
+ [ <?php _e( 'Server', 'wpforo' ); ?> ]
21
+ <?php else: ?>
22
+ [ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=debug&view=server' ) ?>" style="text-decoration: none; outline: none;box-shadow:none;"><?php _e( 'Server', 'wpforo' ); ?></a> ]
23
+ <?php endif; ?>
24
+
25
+ <?php if( $view == 'issues' ): ?>
26
+ [ <?php _e( 'Errors & Issues', 'wpforo' ); ?> ]
27
+ <?php else: ?>
28
+ [ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=debug&view=issues' ) ?>" style="text-decoration: none; outline: none;box-shadow:none;"><?php _e( 'Errors & Issues', 'wpforo' ); ?></a> ]
29
+ <?php endif; ?>
30
+ </h3>
31
+ <div style="margin-top:10px; clear:both;">
32
+
33
+ <table style="width:100%;">
34
+ <tbody style="padding:10px;">
35
+ <?php if( $view == 'user' || $view == '' ): ?>
36
+ <tr>
37
+ <td>
38
+ <h4>
39
+ <?php _e( 'User Data', 'wpforo' ) ?> &nbsp;&nbsp; | &nbsp;&nbsp;
40
+ <?php
41
+ $wpfu = ( wpfval( $_POST, 'wpfu' ) ) ? intval( $_POST['wpfu'] ) : WPF()->current_userid;
42
+ $user = WPF()->member->get_member( $wpfu );
43
+ $usermeta = get_user_meta( $wpfu );
44
+ ?>
45
+ <form method="POST" style="display: inline-block">
46
+ <?php _e( 'User ID', 'wpforo' ) ?>: &nbsp; <input style="display: inline-block; font-size: 12px; padding: 3px 10px; width: 80px; vertical-align: middle;" type="text" name="wpfu" placeholder="<?php _e( 'User ID', 'wpforo' ) ?>" value="<?php echo intval( $wpfu ) ?>">
47
+ <input style="display: inline-block; font-size: 12px; line-height: 20px; height: auto; vertical-align:middle;" type="submit" class="button" value="<?php _e( 'Display User Data', 'wpforo' ); ?>"/>
48
+ </form>
49
+ </h4>
50
+ </td>
51
+ </tr>
52
+ <?php if( ! empty( $user ) ): ?>
53
+ <tr>
54
+ <td><?php echo wpforo_display_array_data( $user ); ?></td>
55
+ </tr>
56
+ <tr>
57
+ <td><h4><?php _e( 'User Meta Data', 'wpforo' ) ?></h4></td>
58
+ </tr>
59
+ <tr>
60
+ <td>
61
+ <?php
62
+ $keys = [ 'nickname', 'rich_editing', 'show_admin_bar_front', 'locale', WPF()->blog_prefix . 'capabilities', WPF()->blog_prefix . 'user_level', 'last_activity', wpforo_prefix( 'read_topics' ), 'session_tokens' ];
63
+ echo wpforo_display_array_data( $usermeta, $keys );
64
+ ?>
65
+ </td>
66
+ </tr>
67
+ <?php if( $wpfu == WPF()->current_userid ): ?>
68
+ <tr>
69
+ <td><h4><?php _e( 'User Cookies', 'wpforo' ) ?></h4></td>
70
+ </tr>
71
+ <tr>
72
+ <td>
73
+ <?php
74
+ $keys = [ wpforo_prefix( 'all_read' ), wpforo_prefix( 'read_topics' ), wpforo_prefix( 'read_forums' ) ];
75
+ echo wpforo_display_array_data( $_COOKIE, $keys );
76
+ ?>
77
+ </td>
78
+ </tr>
79
+ <?php endif; ?>
80
+ <?php else: ?>
81
+ <tr>
82
+ <td><p style="padding: 0px 20px;"><?php _e( 'No user found.', 'wpforo' ) ?></p></td>
83
+ </tr>
84
+ <?php endif; ?>
85
+ <?php endif; ?>
86
+ <?php if( $view == 'server' ): ?>
87
+ <tr>
88
+ <td><h4><?php _e( 'Server Information', 'wpforo' ) ?></h4></td>
89
+ </tr>
90
+ <tr>
91
+ <td>
92
+ <table class="wpf-table-data" style="margin: 10px; width: 98%;">
93
+ <tr class="wpf-dw-tr">
94
+ <td class="wpf-dw-td">USER AGENT</td>
95
+ <td class="wpf-dw-td-value"><?php echo esc_html( $_SERVER['HTTP_USER_AGENT'] ) ?></td>
96
+ </tr>
97
+ <tr class="wpf-dw-tr">
98
+ <td class="wpf-dw-td">Web Server</td>
99
+ <td class="wpf-dw-td-value"><?php echo $_SERVER['SERVER_SOFTWARE'] ?></td>
100
+ </tr>
101
+ <tr class="wpf-dw-tr">
102
+ <td class="wpf-dw-td">PHP Version</td>
103
+ <td class="wpf-dw-td-value"><?php echo phpversion(); ?></td>
104
+ </tr>
105
+ <tr class="wpf-dw-tr">
106
+ <td class="wpf-dw-td">MySQL Version</td>
107
+ <td class="wpf-dw-td-value"><?php echo WPF()->db->get_var( "SELECT VERSION()" ); ?></td>
108
+ </tr>
109
+ <tr class="wpf-dw-tr">
110
+ <td class="wpf-dw-td">PHP Max Post Size</td>
111
+ <td class="wpf-dw-td-value"><?php echo ini_get( 'post_max_size' ); ?></td>
112
+ </tr>
113
+ <tr class="wpf-dw-tr">
114
+ <td class="wpf-dw-td">PHP Max Upload Size</td>
115
+ <td class="wpf-dw-td-value"><?php echo ini_get( 'upload_max_filesize' ); ?></td>
116
+ </tr>
117
+ <tr class="wpf-dw-tr">
118
+ <td class="wpf-dw-td">PHP Memory Limit</td>
119
+ <td class="wpf-dw-td-value"><?php echo ini_get( 'memory_limit' ); ?></td>
120
+ </tr>
121
+ <tr class="wpf-dw-tr">
122
+ <td class="wpf-dw-td">PHP DateTime Class</td>
123
+ <td class="wpf-dw-td-value" style="line-height: 18px!important;">
124
+ <?php echo ( class_exists( 'DateTime' ) && class_exists( 'DateTimeZone' ) && method_exists( 'DateTime', 'setTimestamp' ) ) ? '<span class="wpf-green">' . __( 'Available', 'wpforo' ) . '</span>' : '<span class="wpf-red">' . __(
125
+ 'The setTimestamp() method of PHP DateTime class is not available. Please make sure you use PHP 5.4 and higher version on your hosting service.',
126
+ 'wpforo'
127
+ ) . '</span> | <a href="http://php.net/manual/en/datetime.settimestamp.php" target="_blank">more info&raquo;</a>'; ?> </td>
128
+ </tr>
129
+ <tr class="wpf-dw-tr">
130
+ <td class="wpf-dw-td">PHP cURL Module</td>
131
+ <td class="wpf-dw-td-value" style="line-height: 18px!important;">
132
+ <?php echo ( function_exists( 'curl_version' ) || extension_loaded( 'curl' ) || in_array( 'curl', get_loaded_extensions() ) ) ? '<span class="wpf-green">' . __( 'Available', 'wpforo' ) . '</span>' : '<span class="wpf-red">' . __( 'Not available', 'wpforo' ) . '</span> <span style="font-size: smaller; color: gray;">( ' . __(
133
+ 'Please contact to your hosting service support team and ask them enable PHP cURL module'
134
+ ) . ' )</span> | <a href="https://deliciousbrains.com/php-curl-how-wordpress-makes-http-requests/" target="_blank">more info&raquo;</a>'; ?> </td>
135
+ </tr>
136
+ <?php if( function_exists( 'apache_get_modules' ) ): ?>
137
+ <tr class="wpf-dw-tr">
138
+ <td class="wpf-dw-td">Permalinks / mod_rewrite</td>
139
+ <td class="wpf-dw-td-value" style="line-height: 18px!important;">
140
+ <?php echo ( in_array( 'mod_rewrite', apache_get_modules() ) ) ? '<span class="wpf-green">' . __( 'Available', 'wpforo' ) . '</span>' : '<span class="wpf-red">' . __( 'Not enabled', 'wpforo' ) . '</span> <span style="font-size: smaller; color: gray;">( ' . __(
141
+ 'Please enable mod_rewrite on your server, this is required for wpForo forum'
142
+ ) . ' )</span> | <a href="https://codex.wordpress.org/Using_Permalinks#mod_rewrite:_.22Pretty_Permalinks.22" target="_blank">more info&raquo;</a>'; ?>
143
+ </td>
144
+ </tr>
145
+ <?php endif; ?>
146
+ <?php do_action( 'wpforo_dashboard_widget_server' ) ?>
147
+ </table>
148
+ </td>
149
+ </tr>
150
+ <?php endif; ?>
151
+ <?php if( $view == 'issues' ): ?>
152
+ <tr>
153
+ <td><h4><?php _e( 'Issues and Recommendations', 'wpforo' ) ?></h4></td>
154
+ </tr>
155
+ <tr>
156
+ <td>
157
+ <?php wpforo_issues() ?>
158
+ </td>
159
+ </tr>
160
+ <tr>
161
+ <td><h4><?php _e( 'Error Logs', 'wpforo' ) ?></h4></td>
162
+ </tr>
163
+ <tr>
164
+ <td>
165
+ <?php
166
+ $error_log_file = rtrim( ABSPATH, '/' ) . '/' . 'error_log';
167
+ if( file_exists( $error_log_file ) ) {
168
+ wpforo_read_file_revers( $error_log_file );
169
+ }
170
+ ?>
171
+ <?php
172
+ $error_log_file = rtrim( ABSPATH, '/' ) . '/wp-content/' . 'debug.log';
173
+ if( file_exists( $error_log_file ) ) {
174
+ wpforo_read_file_revers( $error_log_file );
175
+ }
176
+ ?>
177
+ <?php
178
+ $error_log_file = rtrim( ABSPATH, '/' ) . '/wp-admin/' . 'error_log';
179
+ if( file_exists( $error_log_file ) ) {
180
+ wpforo_read_file_revers( $error_log_file );
181
+ }
182
+ ?>
183
+ &nbsp;
184
+ </td>
185
+ </tr>
186
+ <?php endif; ?>
187
+ </tbody>
188
+ </table>
189
+ </div>
190
+ </div>
191
+ </form>
192
+ <?php
193
+ function wpforo_display_array_data( $array, $keys = [] ) {
194
+ $html = '';
195
+ foreach( $array as $k => $v ) {
196
+ if( ( ! empty( $keys ) && ! in_array( $k, $keys ) && strpos( $k, 'capabilities' ) === false ) || $k == 'user_pass' ) continue;
197
+ if( is_serialized( $v ) || is_array( $v ) ) {
198
+ $v = ( is_array( $v ) ) ? $v : @unserialize( $v );
199
+ $v_html = '';
200
+ if( is_array( $v ) && ! empty( $v ) ) {
201
+ foreach( $v as $kk => $vv ) {
202
+ if( is_serialized( $vv ) || is_array( $vv ) ) {
203
+ $v_html = '';
204
+ $vv = ( is_array( $vv ) ) ? $vv : unserialize( $vv );
205
+ if( $k === wpforo_prefix( 'read_topics' ) ) {
206
+ $v_html .= "<span class='wpf-sb-data'>" . count( $vv ) . "</span> ";
207
+ } else {
208
+ if( is_array( $vv ) && ! empty( $vv ) ) {
209
+ foreach( $vv as $kkk => $vvv ) {
210
+ if( is_array( $vvv ) ) $vvv = implode( ' &nbsp; | &nbsp; ', $vvv );
211
+ $v_html .= "<span class='wpf-sb-data'><key>" . $kkk . ':</key> <value>' . wp_unslash( $vvv ) . "</value></span> &nbsp; | &nbsp; ";
212
+ }
213
+ }
214
+ }
215
+ } else {
216
+ $v_html .= "<span class='wpf-sb-data'><key>" . $kk . ':</key> <value>' . $vv . "</value></span> &nbsp; | &nbsp; ";
217
+ }
218
+ }
219
+ }
220
+ $v = $v_html;
221
+ }
222
+ if( $v == '' ) $v = 'null';
223
+ $html .= "<div class='wpf-data'><key>" . $k . ':</key> <value>' . wp_unslash( $v ) . "</value></div>";
224
+ }
225
+
226
+ return $html . "<div style='clear:both'></div>";
227
+ }
228
+
229
+ function wpforo_issues() {
230
+
231
+ $issues = [];
232
+ ##########################################################
233
+ //Usergroups//////////////////////////////////////////////
234
+ $guest_exists = WPF()->db->get_var( "SELECT `groupid` FROM `" . WPF()->tables->usergroups . "` WHERE `groupid` = 4" );
235
+ if( ! $guest_exists ) {
236
+ $issues['usergroup']['guest']['level'] = 3;
237
+ $issues['usergroup']['guest']['message'] = __( 'The default Guest usergroup is not found!', 'wpforo' );
238
+ $issues['usergroup']['guest']['solution'] = __( 'Please execute this SQL in your Website Hosting cPanel > phpMyAdmin Database Manager', 'wpforo' ) . ": <br><pre style='white-space: pre-line; word-break: break-all; color:#006600'>" . 'INSERT INTO `' . WPF()->tables->usergroups . '` (`groupid`, `name`, `cans`, `description`, `utitle`, `role`, `access`, `color`, `visible`, `secondary`) VALUES
239
+ (4, \'Guest\', \'a:32:{s:2:\"mf\";s:1:\"0\";s:2:\"ms\";s:1:\"0\";s:2:\"mt\";s:1:\"0\";s:2:\"mp\";s:1:\"0\";s:3:\"mth\";s:1:\"0\";s:2:\"vm\";s:1:\"0\";s:3:\"aum\";s:1:\"0\";s:2:\"em\";s:1:\"0\";s:3:\"vmg\";s:1:\"0\";s:3:\"aup\";s:1:\"0\";s:4:\"vmem\";s:1:\"1\";s:4:\"vprf\";s:1:\"1\";s:4:\"vpra\";s:1:\"1\";s:4:\"vprs\";s:1:\"0\";s:2:\"bm\";s:1:\"0\";s:2:\"dm\";s:1:\"0\";s:3:\"upa\";s:1:\"0\";s:3:\"ups\";s:1:\"0\";s:2:\"va\";s:1:\"1\";s:3:\"vmu\";s:1:\"0\";s:3:\"vmm\";s:1:\"0\";s:3:\"vmt\";s:1:\"1\";s:4:\"vmct\";s:1:\"1\";s:3:\"vmr\";s:1:\"1\";s:3:\"vmw\";s:1:\"0\";s:4:\"vmsn\";s:1:\"1\";s:4:\"vmrd\";s:1:\"1\";s:3:\"vml\";s:1:\"1\";s:3:\"vmo\";s:1:\"1\";s:3:\"vms\";s:1:\"1\";s:4:\"vmam\";s:1:\"1\";s:4:\"vwpm\";s:1:\"0\";}\', \'\', \'Guest\', \'\', \'read_only\', \'#222222\', 0, 0);
240
+ ' . "</pre>";
241
+ }
242
+
243
+ $perms = [];
244
+ $nonsec = [
245
+ 'mf' => __( 'Dashboard - Manage Forums', 'wpforo' ),
246
+ 'ms' => __( 'Dashboard - Manage Settings', 'wpforo' ),
247
+ 'mt' => __( 'Dashboard - Manage Tools', 'wpforo' ),
248
+ 'vm' => __( 'Dashboard - Manage Members', 'wpforo' ),
249
+ 'aum' => __( 'Dashboard - Moderate Topics & Posts', 'wpforo' ),
250
+ 'vmg' => __( 'Dashboard - Manage Usergroups', 'wpforo' ),
251
+ 'mp' => __( 'Dashboard - Manage Phrases', 'wpforo' ),
252
+ 'mth' => __( 'Dashboard - Manage Themes', 'wpforo' ),
253
+ 'em' => __( 'Dashboard - Can edit member', 'wpforo' ),
254
+ 'bm' => __( 'Dashboard - Can ban member', 'wpforo' ),
255
+ 'dm' => __( 'Dashboard - Can delete member', 'wpforo' ),
256
+ 'vmu' => __( 'Front - Can view member username', 'wpforo' ),
257
+ ];
258
+ $default_ug = WPF()->usergroup->default_groupid;
259
+ $default_ug_cans = WPF()->db->get_var( "SELECT `cans` FROM `" . WPF()->tables->usergroups . "` WHERE `groupid` = " . intval( $default_ug ) );
260
+ $default_ug_cans = unserialize( $default_ug_cans );
261
+ if( ! empty( $default_ug_cans ) ) {
262
+ foreach( $default_ug_cans as $key => $value ) {
263
+ if( wpfkey( $nonsec, $key ) && $value == 1 ) {
264
+ $perms[] = $nonsec[ $key ];
265
+ }
266
+ }
267
+ if( ! empty( $perms ) ) {
268
+ $cans = '<ul style="list-style:disc; margin:10px 20px; font-size:12px; line-height:14px;"><li>' . implode( '</li><li>', $perms ) . '</li></ul>';
269
+ $issues['usergroup']['default_perm']['level'] = 3;
270
+ $issues['usergroup']['default_perm']['message'] = __( 'New registered users get access to forum settings!', 'wpforo' );
271
+ $issues['usergroup']['default_perm']['solution'] = __( sprintf( 'Please navigate to Forums > Usergroups admin page, edit the "Default" usergroup (ID = %s) and uncheck/disable following permission(s):', $default_ug ), 'wpforo' ) . $cans;
272
+ }
273
+ }
274
+ #########################################################
275
+ //Plugin Conflicts///////////////////////////////////////
276
+ if( class_exists( 'autoptimizeCache' ) ) {
277
+ $autopt = get_option( 'autoptimize_js_exclude' );
278
+ if( $autopt && strpos( $autopt, 'wp-includes/js/tinymce' ) === false ) {
279
+ $issues['conflicts']['autoptimize']['level'] = 3;
280
+ $issues['conflicts']['autoptimize']['message'] = __( 'Conflict with Autoptimize plugin!', 'wpforo' );
281
+ $issues['conflicts']['autoptimize']['solution'] = __( 'Please navigate to Settings > Autoptimize > Main Tab, click on top right [Show advanced settings] button, find "Exclude scripts from Autoptimize" option, add this JS path <code>,wp-includes/js/tinymce</code>, than click on [Save Changes and Empty Cache] button bellow.', 'wpforo' );
282
+ }
283
+ }
284
+ #########################################################
285
+ //Email Issues //////////////////////////////////////////
286
+ $email = "test@example.com";
287
+ $subject = "Email Test";
288
+ $message = "This is a mail testing email function on server";
289
+ if( ! wp_mail( $email, $subject, $message ) ) {
290
+ $issues['email']['wp_mail']['level'] = 3;
291
+ $issues['email']['wp_mail']['message'] = __( 'WordPress Email sending function wp_mail() doesn\'t work!', 'wpforo' );
292
+ $issues['email']['wp_mail']['solution'] = __( 'In most cases this is a server issue. We recommend you contact to your hosting service support team or open a support topic in wordpress.org support forum. Also there are many good articles regarding this issue in web. For example ', 'wpforo' ) . ' - <a href="https://www.wpbeginner.com/wp-tutorials/how-to-fix-wordpress-not-sending-email-issue/">' . __( 'How to Fix WordPress Not Sending Email Issue', 'wpforo' ) . '</a>';
293
+ }
294
+ #########################################################
295
+ //Other Issues //////////////////////////////////////////
296
+ $phrases = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->phrases . "`" );
297
+ if( ! $phrases ) {
298
+ $issues['other']['empty_phrase_table']['level'] = 3;
299
+ $issues['other']['empty_phrase_table']['message'] = __( 'wpForo phrases are missing!', 'wpforo' );
300
+ $issues['other']['empty_phrase_table']['solution'] = __( 'Please download wpForo Phrases XML compressed file, unzip it, find english.xml file, navigate to Forums > Settings > General Tab and import it using [Add New] button of "XML Based Language" option.', 'wpforo' ) . ' [ <a href="https://wpforo.com/2wpf/english.xml.zip">' . __( 'Download wpForo Phrases', 'wpforo' ) . '</a> ]';
301
+ }
302
+
303
+ echo '<table class="wpf-table-data" style="margin: 10px; width: 98%;">';
304
+ if( ! empty( $issues ) ) {
305
+ foreach( $issues as $key => $issue ) {
306
+ if( ! empty( $issue ) ) {
307
+ echo '<tr><td colspan="6" style="background:#d86868; color: #ffffff;font-size:14px; line-height:26px"><b>' . strtoupper( $key ) . '</b></td></tr>';
308
+ foreach( $issue as $data ) {
309
+ echo '<tr>';
310
+ echo '<td class="wpf-ilevel-' . intval( $data['level'] ) . '" style="vertical-align: top;width:25%; font-size:15px; padding:10px; line-height: 22px;">' . $data['message'] . '</td>';
311
+ echo '<td style="vertical-align: top;font-size:13px;line-height:20px;padding:10px;">' . $data['solution'] . '</td>';
312
+ echo '</tr>';
313
+ }
314
+ }
315
+ }
316
+ } else {
317
+ echo '<tr><td><p>&nbsp;&nbsp;&nbsp;' . __( 'No issues found', 'wpforo' ) . '<p></td></tr>';
318
+ }
319
+ echo '</table>';
320
+ }
321
+
322
+ function wpforo_read_file_revers( $file ) {
323
+ $result = [];
324
+ $numRows = 100;
325
+ $handle = fopen( $file, "r" );
326
+ while( ! feof( $handle ) ) {
327
+ array_push( $result, fgets( $handle, 4096 ) );
328
+ if( count( $result ) > $numRows ) array_shift( $result );
329
+ }
330
+ $result = array_filter( $result );
331
+ $result = array_reverse( $result );
332
+ if( ! empty( $result ) ) {
333
+ echo '<h3 style="padding:5px 20px;">' . __( 'Error Log File', 'wpforo' ) . ': <code style="font-size:14px;">' . $file . '</code><h3>';
334
+ echo '<ul style="list-style:disc; margin:20px 30px;">';
335
+ foreach( $result as $error ) {
336
+ echo '<li style="border-bottom:1px dotted #cccccc; font-size:12px;line-height:16px; padding:0px 0px 3px 0px">' . $error . '</li>';
337
+ }
338
+ echo '</ul>';
339
+ } else {
340
+ echo '<p>' . __( 'No errors found', 'wpforo' ) . '</p>';
341
+ }
342
+
343
+ }
{wpf-admin/xml → admin/tools-tabs}/index.html RENAMED
File without changes
admin/tools-tabs/misc.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! current_user_can( 'administrator' ) ) exit;
5
+ ?>
6
+
7
+ <form action="" method="POST" class="validate">
8
+ <?php wp_nonce_field( 'wpforo-tools-misc' ) ?>
9
+ <input type="hidden" name="wpfaction" value="misc_options_save">
10
+ <div class="wpf-tool-box" style="width:98%; margin-left: 1%; margin-top: 15px; border: none;">
11
+ <h3><?php _e( 'Admin Note', 'wpforo' ); ?>
12
+ <p class="wpf-info"></p>
13
+ </h3>
14
+ <div style="margin-top:10px; clear:both;">
15
+ <table style="width:100%;">
16
+ <tbody style="padding:10px;">
17
+ <tr>
18
+ <td colspan="2">
19
+ <label style="padding-bottom:5px; display:block; font-weight: 600;"><?php _e( 'Admin message on forum front page', 'wpforo' ); ?></label>
20
+ <p class="wpf-info"><?php _e( 'If you have something important to say on forum front page, you can use this editor. The text will be displayed under forum breadcrumb menu, above forum and topic titles.', 'wpforo' ); ?></p>
21
+ <br>
22
+ <?php
23
+ $value = WPF()->tools_misc['admin_note'];
24
+ $args = [
25
+ 'teeny' => false,
26
+ 'media_buttons' => true,
27
+ 'textarea_rows' => '8',
28
+ 'tinymce' => true,
29
+ 'quicktags' => [ 'buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close' ],
30
+ 'textarea_name' => 'wpforo_tools_misc[admin_note]',
31
+ ];
32
+ wp_editor( wp_unslash( $value ), 'wpforo_tools_misc_admin_note', $args ); ?>
33
+
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <th style="vertical-align: top; padding: 10px;">
38
+ <label style="font-weight: 600"><?php _e( 'Display for Usergroups', 'wpforo' ); ?></label>
39
+ </th>
40
+ <td>
41
+ <?php
42
+ $usergroups = WPF()->usergroup->get_usergroups();
43
+ $admin_note_groups = WPF()->tools_misc['admin_note_groups'];
44
+ ?>
45
+ <?php if( ! empty( $usergroups ) ): ?>
46
+ <?php foreach( $usergroups as $usergroup ): ?>
47
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
48
+ <input type="checkbox"
49
+ name="wpforo_tools_misc[admin_note_groups][]"
50
+ value="<?php echo intval( $usergroup['groupid'] ) ?>"
51
+ <?php echo ( ! empty( $admin_note_groups ) && in_array( $usergroup['groupid'], $admin_note_groups ) ) ? 'checked="checked"' : ''; ?>>&nbsp;
52
+ <?php echo esc_html( $usergroup['name'] ); ?>
53
+ </label>
54
+ <?php endforeach; ?>
55
+ <?php endif; ?>
56
+ </td>
57
+ </tr>
58
+ <tr>
59
+ <th style="vertical-align: top; padding: 10px;">
60
+ <label style="font-weight: 600"><?php _e( 'Display on forum pages', 'wpforo' ); ?></label>
61
+ </th>
62
+ <td>
63
+ <?php $pages = WPF()->tools_misc['admin_note_pages'];
64
+ if( empty( $pages ) ) $pages = []; ?>
65
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
66
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'forum', $pages ) ) echo 'checked' ?> value="forum" type="checkbox">&nbsp;
67
+ <?php _e( 'Forums', 'wpforo' ) ?>
68
+ </label>
69
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
70
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'topic', $pages ) ) echo 'checked' ?> value="topic" type="checkbox">&nbsp;
71
+ <?php _e( 'Forum (topic list)', 'wpforo' ) ?>
72
+ </label>
73
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
74
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'post', $pages ) ) echo 'checked' ?> value="post" type="checkbox">&nbsp;
75
+ <?php _e( 'Topic (post list)', 'wpforo' ) ?>
76
+ </label>
77
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
78
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'recent', $pages ) ) echo 'checked' ?> value="recent" type="checkbox">&nbsp;
79
+ <?php _e( 'Recent Posts', 'wpforo' ) ?>
80
+ </label>
81
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
82
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'tags', $pages ) ) echo 'checked' ?> value="recent" type="checkbox">&nbsp;
83
+ <?php _e( 'Tags', 'wpforo' ) ?>
84
+ </label>
85
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
86
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'members', $pages ) ) echo 'checked' ?> value="members" type="checkbox">&nbsp;
87
+ <?php _e( 'Members', 'wpforo' ) ?>
88
+ </label>
89
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
90
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'profile', $pages ) ) echo 'checked' ?> value="profile" type="checkbox">&nbsp;
91
+ <?php _e( 'Profile Home', 'wpforo' ) ?>
92
+ </label>
93
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
94
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'account', $pages ) ) echo 'checked' ?> value="account" type="checkbox">&nbsp;
95
+ <?php _e( 'Profile Account', 'wpforo' ) ?>
96
+ </label>
97
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
98
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'activity', $pages ) ) echo 'checked' ?> value="activity" type="checkbox">&nbsp;
99
+ <?php _e( 'Profile Activity', 'wpforo' ) ?>
100
+ </label>
101
+ <label style="min-width: 30%; display: inline-block; padding-bottom: 5px;">
102
+ <input name="wpforo_tools_misc[admin_note_pages][]" <?php if( in_array( 'subscriptions', $pages ) ) echo 'checked' ?> value="subscriptions" type="checkbox">&nbsp;
103
+ <?php _e( 'Profile Subscriptions', 'wpforo' ) ?>
104
+ </label>
105
+
106
+ </td>
107
+ </tr>
108
+ </tbody>
109
+ </table>
110
+ </div>
111
+ </div>
112
+ <div class="wpforo_settings_foot" style="clear:both; margin-top:20px;">
113
+ <input type="submit" class="button button-primary" value="<?php _e( 'Update Options', 'wpforo' ); ?>"/>
114
+ <input type="submit" class="button" value="<?php _e( 'Reset Options', 'wpforo' ); ?>" name="reset" onclick="return confirm('<?php wpforo_phrase( 'Do you really want to reset options?' ) ?>')"/>
115
+ </div>
116
+ </form>
admin/tools-tabs/tables.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if( ! defined( 'ABSPATH' ) ) exit;
4
+ if( ! current_user_can( 'administrator' ) ) exit;
5
+
6
+ function wpforo_debug_page_ajax_actions() { ?>
7
+ <script type="text/javascript">
8
+ jQuery(document).ready(function ($) {
9
+ $('#wpf-debug-tables-solve-button').on('click', function () {
10
+ $(this).prepend('<i class="fas fa-spinner fa-spin"></i>')
11
+ $.ajax({
12
+ type: 'POST',
13
+ url: ajaxurl,
14
+ data: {
15
+ action: 'wpforo_update_database',
16
+ _wpnonce: '<?php echo wp_create_nonce( 'wpforo_update_database' ) ?>'
17
+ }
18
+ }).done(function () {
19
+ window.location.reload()
20
+ })
21
+ })
22
+ })
23
+ </script>
24
+ <?php
25
+ }
26
+
27
+ add_action( 'admin_footer', 'wpforo_debug_page_ajax_actions' );
28
+ ?>
29
+ <div class="wpf-tool-box" style="width:98%; margin-left: 1%; border: none;">
30
+ <table style="width:100%;">
31
+ <tbody style="padding:10px;">
32
+ <?php $problems = wpforo_database_check(); ?>
33
+ <?php if( $problems ): ?>
34
+ <tr>
35
+ <td><h3 style="color: #aa0000; margin: 0; border-bottom: none; padding: 15px 0 0 0"><span class="dashicons dashicons-warning" style="vertical-align: sub;"></span>&nbsp;<?php _e( 'Problems Found in Database', 'wpforo' ) ?></h3></td>
36
+ </tr>
37
+ <tr style="background-color: transparent;">
38
+ <td style="padding-bottom: 50px;">
39
+ <div style="font-size: 14px;">
40
+ <div style="font-weight: 600; display: flex; flex-direction: row; justify-content: space-between; padding: 5px 10px; border-bottom: 1px solid #cc0000; margin-bottom: 5px; color: #333333; background-color: #f9f9f9;">
41
+ <div style="width: 30%"><?php _e( 'Table name', 'wpforo' ); ?></div>
42
+ <div style="text-align: left; flex-grow: 1;"><?php _e( 'Problem description', 'wpforo' ); ?></div>
43
+ </div>
44
+ <?php foreach( $problems as $table_name => $problem ): ?>
45
+ <?php if( wpfval( $problem, 'fields' ) ): ?>
46
+ <?php foreach( $problem['fields'] as $problem_fields ): ?>
47
+ <div style="display: flex; flex-direction: row; justify-content: space-between; padding: 5px 10px; background-color: #ffeffa; color: #aa0000; margin-bottom: 3px;">
48
+ <div style="width: 30%; font-weight: 600;"><?php _e( 'Table:', 'wpforo' ); ?><?php echo $table_name; ?></div>
49
+ <div style="text-align: left; flex-grow: 1;"><?php _e( 'Missing fields: ', 'wpforo' ); ?> &nbsp;<code><?php echo implode( ', ', $problem_fields ); ?></code></div>
50
+ </div>
51
+ <?php endforeach; ?>
52
+ <?php endif; ?>
53
+ <?php if( wpfval( $problem, 'keys' ) ): ?>
54
+ <?php foreach( $problem['keys'] as $problem_keys ): ?>
55
+ <div style="display: flex; flex-direction: row; justify-content: space-between; padding: 5px 10px; background-color: #ffeffa; color: #aa0000; margin-bottom: 3px;">
56
+ <div style="width: 30%; font-weight: 600;"><?php _e( 'Table:', 'wpforo' ); ?><?php echo $table_name; ?></div>
57
+ <div style="text-align: left; flex-grow: 1;"><?php _e( 'Missing keys: ', 'wpforo' ); ?> &nbsp;<code><?php echo implode( ', ', $problem_keys ); ?></code></div>
58
+ </div>
59
+ <?php endforeach; ?>
60
+ <?php endif; ?>
61
+ <?php if( wpfkey( $problem, 'exists' ) ): ?>
62
+ <?php if( $problem['exists'] ): ?>
63
+ <div style="display: flex; flex-direction: row; justify-content: space-between; padding: 5px 10px; background-color: #ffeffa;; color: #aa0000; margin-bottom: 3px;">
64
+ <div style="width: 30%; font-weight: 600;"><?php _e( 'Table:', 'wpforo' ); ?><?php echo $table_name; ?></div>
65
+ <div style="text-align: left; flex-grow: 1;"><?php _e( 'Doesn\'t exists', 'wpforo' ); ?></div>
66
+ </div>
67
+ <?php endif; ?>
68
+ <?php endif; ?>
69
+ <?php endforeach; ?>
70
+ <div style="display:flex; flex-direction: row; justify-content: space-between; border-top: 1px dashed #cccccc; padding-top: 10px; text-align: right; margin-top: 10px; font-weight: normal; font-size: 13px;">
71
+ <?php $update_db_url = wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=tables&wpfaction=database_update' ), 'wpforo_update_database' ); ?>
72
+ <div>
73
+ <a href="<?php echo esc_url( $update_db_url ); ?>" class="button button-large" style="font-size:12px;">
74
+ <?php _e( 'Recheck DB', 'wpforo' ); ?>
75
+ </a>
76
+ </div>
77
+ <div style="padding: 0 30px;">
78
+ <span id="wpf-debug-tables-solve-button" class="button button-primary button-large" style="font-size:14px;">
79
+ <?php _e( 'Solve database problems', 'wpforo' ); ?>
80
+ </span>
81
+ </div>
82
+ </div>
83
+ <div style=" margin-top: 0px; font-weight: normal; font-size: 13px;">
84
+ <p style="font-weight: 600; font-size: 16px; margin-bottom: 2px;"><?php _e( 'IMPORTANT!', 'wpforo' ) ?></p>
85
+ <p style="font-size: 14px; line-height: 1.5; margin-top: 2px;"><?php printf(
86
+ __( 'If the %s button doesn\'t solve the issues. Please use the SQl commands below in your hosting service cPanel > phpMyAdmin Database Manager > WordPress Database > SQL Tab. In case you\'re not familiar with hosting service tools, please contact to your hosting service support team and forward them this message with the SQL command.', 'wpforo' ),
87
+ '<b style="color: #0085ba; font-size:13px;font-family: Courier">[' . __( 'Solve database problems', 'wpforo' ) . ']</b>'
88
+ ) ?></p>
89
+ <p style="font-size: 14px; font-weight: 600;"><?php _e( 'Problem fixer SQL commands:', 'wpforo' ) ?></p>
90
+ <pre style="padding: 10px; line-height: 1.4; border: 1px dashed #dd0000; background-color: #fffdea; color: #000000;"><?php
91
+ if( ! empty( $problems ) ) {
92
+ echo 'SET AUTOCOMMIT = 0;<br />';
93
+ $SQL = wpforo_database_fixer( $problems );
94
+ if( wpfval( $SQL, 'fields' ) ) {
95
+ foreach( $SQL['fields'] as $query ) echo $query . '<br>';
96
+ }
97
+ if( wpfval( $SQL, 'keys' ) ) {
98
+ foreach( $SQL['keys'] as $query ) echo $query . '<br>';
99
+ }
100
+ if( wpfval( $SQL, 'tables' ) ) {
101
+ foreach( $SQL['tables'] as $query ) echo $query . '<br><br>';
102
+ }
103
+ echo ';SET AUTOCOMMIT = 1;';
104
+ }
105
+ ?></pre>
106
+ </div>
107
+ </div>
108
+ </td>
109
+ </tr>
110
+ <?php else: ?>
111
+ <tr>
112
+ <td><h3 style="color: #00aa00; margin: 0; border-bottom: none; padding: 15px 0 30px 0"><span class="dashicons dashicons-shield" style="vertical-align: sub;"></span>&nbsp;<?php _e( 'No Problems Found in Database', 'wpforo' ) ?></h3></td>
113
+ </tr>
114
+ <?php endif; ?>
115
+ <tr style="background-color: transparent;">
116
+ <td><h3 style=" border-bottom: none;"><?php _e( 'Database Tables', 'wpforo' ) ?></h3></td>
117
+ </tr>
118
+ <tr>
119
+ <td><?php foreach( WPF()->tables as $table ) {
120
+ wpforo_table_info( $table );
121
+ } ?></td>
122
+ </tr>
123
+ </tbody>
124
+ </table>
125
+ </div>
126
+
127
+
128
+ <?php
129
+ function wpforo_table_info( $table ) {
130
+ $_table_exists = WPF()->db->get_var( "SHOW TABLES LIKE '" . esc_sql( $table ) . "'" );
131
+ if( $_table_exists ) {
132
+ $result = WPF()->db->get_results( "SHOW FULL COLUMNS FROM " . esc_sql( $table ), ARRAY_A );
133
+ $count = WPF()->db->get_var( "SELECT COUNT(*) FROM " . esc_sql( $table ) );
134
+ $status = WPF()->db->get_row( "CHECK TABLE " . esc_sql( $table ), ARRAY_A );
135
+ echo '<table class="wpf-main-table-data"><tr><td>';
136
+ echo '<table class="wpf-table-data wpf-tbl">';
137
+ echo '<tr><td colspan="6" style="background:#999999; color: #ffffff;font-size:14px; line-height:26px"><b>' . $table . '</b> &nbsp;|&nbsp; Rows: ' . $count . ' &nbsp;|&nbsp; Status: ' . $status['Msg_text'] . '</td></tr>';
138
+ foreach( $result as $info ) {
139
+ echo '<tr>';
140
+ echo '<td>' . $info['Field'] . '</td>';
141
+ echo '<td>' . $info['Type'] . '</td>';
142
+ echo '<td>' . $info['Collation'] . '</td>';
143
+ echo '<td>' . ( $info['Null'] == 'NO' ? '-' : 'NULL' ) . '</td>';
144
+ echo '<td>' . $info['Key'] . '</td>';
145
+ echo '<td>' . $info['Default'] . '</td>';
146
+ echo '</tr>';
147
+ }
148
+ echo '</table>';
149
+ echo '</td>';
150
+ echo '<td>';
151
+ $result = WPF()->db->get_results( "SHOW INDEXES FROM " . esc_sql( $table ), ARRAY_A );
152
+
153
+ echo '<table class="wpf-table-data">';
154
+ echo '<tr><td colspan="6" style="background:#BBBBBB; color: #ffffff;font-size:14px; line-height:26px"><b>Indexes</b></td></tr>';
155
+ $indexes = [];
156
+ foreach( $result as $info ) {
157
+ $indexes[ $info['Key_name'] ]['Key_name'] = $info['Key_name'];
158
+ $indexes[ $info['Key_name'] ]['Column_name'][] = $info['Column_name'];
159
+ $indexes[ $info['Key_name'] ]['Non_unique'] = $info['Non_unique'];
160
+ $indexes[ $info['Key_name'] ]['Index_type'] = $info['Index_type'];
161
+ }
162
+ foreach( $indexes as $info ) {
163
+ echo '<tr>';
164
+ echo '<td>' . $info['Key_name'] . '</td>';
165
+ echo '<td>' . implode( ', ', $info['Column_name'] ) . '</td>';
166
+ echo '<td>' . ( $info['Non_unique'] ? '0' : 'Un' ) . '</td>';
167
+ echo '<td>' . $info['Index_type'] . '</td>';
168
+ echo '</tr>';
169
+ }
170
+ echo '</table>';
171
+
172
+ echo '</td>';
173
+ echo '</tr>';
174
+ echo '</table>';
175
+ } else {
176
+ echo '<h3>Table <span style="color: red;">' . $table . '</span> doesn\'t exist</h3>';
177
+ }
178
+ }
{wpf-assets → assets}/addons/ad-manager/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/ad-manager/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/ad-manager/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/attachments/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/attachments/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/attachments/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/cross/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/cross/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/cross/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/embeds/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/embeds/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/embeds/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/giphy/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/giphy/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/mycred/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/mycred/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/mycred/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/pm/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/pm/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/pm/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/polls/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/polls/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/polls/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/prefix/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/prefix/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/syntax/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/syntax/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/tcf/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/tcf/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/tenor/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/tenor/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/ucf/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/ucf/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/ucf/index.html RENAMED
File without changes
{wpf-assets → assets}/addons/wpforo-emoticons/header-off.png RENAMED
File without changes
{wpf-assets → assets}/addons/wpforo-emoticons/header.png RENAMED
File without changes
{wpf-assets → assets}/addons/wpforo-emoticons/index.html RENAMED
File without changes
{wpf-assets → assets}/css/font-awesome/LICENSE.txt RENAMED
File without changes
assets/css/font-awesome/css/fontawesome-all.min.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*!
2
+ * Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com
3
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4
+ * Copyright 2022 Fonticons, Inc.
5
+ */
6
+ .fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-duotone,.fa-light,.fa-regular,.fa-solid,.fa-thin,.fab,.fad,.fal,.far,.fas,.fat{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,none));transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)}.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-a:before{content:"\41"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-anchor:before{content:"\f13d"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-anchor-lock:before{content:"\e4ad"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-ankh:before{content:"\f644"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-archway:before{content:"\f557"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-arrow-trend-down:before{content:"\e097"}.fa-arrow-trend-up:before{content:"\e098"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-arrows-spin:before{content:"\e4bb"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-asterisk:before{content:"\2a"}.fa-at:before{content:"\40"}.fa-atom:before{content:"\f5d2"}.fa-audio-description:before{content:"\f29e"}.fa-austral-sign:before{content:"\e0a9"}.fa-award:before{content:"\f559"}.fa-b:before{content:"\42"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-backward:before{content:"\f04a"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-bahai:before{content:"\f666"}.fa-baht-sign:before{content:"\e0ac"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-barcode:before{content:"\f02a"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-bed:before{content:"\f236"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-bell:before{content:"\f0f3"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bicycle:before{content:"\f206"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blog:before{content:"\f781"}.fa-bold:before{content:"\f032"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-book-bookmark:before{content:"\e0bb"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-bookmark:before{content:"\f02e"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-bore-hole:before{content:"\e4c3"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-bottle-water:before{content:"\e4c5"}.fa-bowl-food:before{content:"\e4c6"}.fa-bowl-rice:before{content:"\e2eb"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes-packing:before{content:"\e4c7"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-bread-slice:before{content:"\f7ec"}.fa-bridge:before{content:"\e4c8"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-bridge-water:before{content:"\e4ce"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broom:before{content:"\f51a"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-brush:before{content:"\f55d"}.fa-bucket:before{content:"\e4cf"}.fa-bug:before{content:"\f188"}.fa-bug-slash:before{content:"\e490"}.fa-bugs:before{content:"\e4d0"}.fa-building:before{content:"\f1ad"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-building-circle-check:before{content:"\e4d2"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-building-flag:before{content:"\e4d5"}.fa-building-lock:before{content:"\e4d6"}.fa-building-ngo:before{content:"\e4d7"}.fa-building-shield:before{content:"\e4d8"}.fa-building-un:before{content:"\e4d9"}.fa-building-user:before{content:"\e4da"}.fa-building-wheat:before{content:"\e4db"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-burst:before{content:"\e4dc"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-c:before{content:"\43"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-week:before{content:"\f784"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-camera-rotate:before{content:"\e0d8"}.fa-campground:before{content:"\f6bb"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-car-on:before{content:"\e4dd"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-car-side:before{content:"\f5e4"}.fa-car-tunnel:before{content:"\e4de"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-cart-plus:before{content:"\f217"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cedi-sign:before{content:"\e0df"}.fa-cent-sign:before{content:"\e3f5"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-charging-station:before{content:"\f5e7"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-chart-column:before{content:"\e0e3"}.fa-chart-gantt:before{content:"\e0e4"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-chart-simple:before{content:"\e473"}.fa-check:before{content:"\f00c"}.fa-check-double:before{content:"\f560"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-child-dress:before{content:"\e59c"}.fa-child-reaching:before{content:"\e59d"}.fa-child-rifle:before{content:"\e4e0"}.fa-children:before{content:"\e4e1"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-circle-nodes:before{content:"\e4e2"}.fa-circle-notch:before{content:"\f1ce"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-city:before{content:"\f64f"}.fa-clapperboard:before{content:"\e131"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clipboard-question:before{content:"\e4e3"}.fa-clipboard-user:before{content:"\f7f3"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-clover:before{content:"\e139"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-code-commit:before{content:"\f386"}.fa-code-compare:before{content:"\e13a"}.fa-code-fork:before{content:"\e13b"}.fa-code-merge:before{content:"\f387"}.fa-code-pull-request:before{content:"\e13c"}.fa-coins:before{content:"\f51e"}.fa-colon-sign:before{content:"\e140"}.fa-comment:before{content:"\f075"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-compress:before{content:"\f066"}.fa-computer:before{content:"\e4e5"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cow:before{content:"\f6c8"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-d:before{content:"\44"}.fa-database:before{content:"\f1c0"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-democrat:before{content:"\f747"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-dharmachakra:before{content:"\f655"}.fa-diagram-next:before{content:"\e476"}.fa-diagram-predecessor:before{content:"\e477"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-diagram-successor:before{content:"\e47a"}.fa-diamond:before{content:"\f219"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-disease:before{content:"\f7fa"}.fa-display:before{content:"\e163"}.fa-divide:before{content:"\f529"}.fa-dna:before{content:"\f471"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-dong-sign:before{content:"\e169"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dove:before{content:"\f4ba"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-download:before{content:"\f019"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-e:before{content:"\45"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elevator:before{content:"\e16d"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-equals:before{content:"\3d"}.fa-eraser:before{content:"\f12d"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-exclamation:before{content:"\21"}.fa-expand:before{content:"\f065"}.fa-explosion:before{content:"\e4e9"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-eye-slash:before{content:"\f070"}.fa-f:before{content:"\46"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-fan:before{content:"\f863"}.fa-faucet:before{content:"\e005"}.fa-faucet-drip:before{content:"\e006"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-ferry:before{content:"\e4ea"}.fa-file:before{content:"\f15b"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-file-audio:before{content:"\f1c7"}.fa-file-circle-check:before{content:"\e493"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-file-circle-plus:before{content:"\e4ee"}.fa-file-circle-question:before{content:"\e4ef"}.fa-file-circle-xmark:before{content:"\e494"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-excel:before{content:"\f1c3"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-file-medical:before{content:"\f477"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-shield:before{content:"\e4f0"}.fa-file-signature:before{content:"\f573"}.fa-file-video:before{content:"\f1c8"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-file-word:before{content:"\f1c2"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-burner:before{content:"\e4f1"}.fa-fire-extinguisher:before{content:"\f134"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-fish:before{content:"\f578"}.fa-fish-fins:before{content:"\e4f2"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flask-vial:before{content:"\e4f3"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-florin-sign:before{content:"\e184"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-folder-closed:before{content:"\e185"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-folder-tree:before{content:"\f802"}.fa-font:before{content:"\f031"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-franc-sign:before{content:"\e18f"}.fa-frog:before{content:"\f52e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-g:before{content:"\47"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-glass-water:before{content:"\e4f4"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-glasses:before{content:"\f530"}.fa-globe:before{content:"\f0ac"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-greater-than:before{content:"\3e"}.fa-greater-than-equal:before{content:"\f532"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-guarani-sign:before{content:"\e19a"}.fa-guitar:before{content:"\f7a6"}.fa-gun:before{content:"\e19b"}.fa-h:before{content:"\48"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-handcuffs:before{content:"\e4f8"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-hands-bound:before{content:"\e4f9"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-hands-clapping:before{content:"\e1a8"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-handshake:before{content:"\f2b5"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-hashtag:before{content:"\23"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-heart-circle-plus:before{content:"\e500"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-helicopter-symbol:before{content:"\e502"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-helmet-un:before{content:"\e503"}.fa-highlighter:before{content:"\f591"}.fa-hill-avalanche:before{content:"\e507"}.fa-hill-rockslide:before{content:"\e508"}.fa-hippo:before{content:"\f6ed"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hourglass-2:before,.fa-hourglass-half:before,.fa-hourglass:before{content:"\f254"}.fa-hourglass-empty:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-house-chimney-user:before{content:"\e065"}.fa-house-chimney-window:before{content:"\e00d"}.fa-house-circle-check:before{content:"\e509"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-house-crack:before{content:"\e3b1"}.fa-house-fire:before{content:"\e50c"}.fa-house-flag:before{content:"\e50d"}.fa-house-flood-water:before{content:"\e50e"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-house-lock:before{content:"\e510"}.fa-house-medical:before{content:"\e3b2"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-house-medical-flag:before{content:"\e514"}.fa-house-signal:before{content:"\e012"}.fa-house-tsunami:before{content:"\e515"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-hurricane:before{content:"\f751"}.fa-i:before{content:"\49"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-images:before{content:"\f302"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-italic:before{content:"\f033"}.fa-j:before{content:"\4a"}.fa-jar:before{content:"\e516"}.fa-jar-wheat:before{content:"\e517"}.fa-jedi:before{content:"\f669"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-jet-fighter-up:before{content:"\e518"}.fa-joint:before{content:"\f595"}.fa-jug-detergent:before{content:"\e519"}.fa-k:before{content:"\4b"}.fa-kaaba:before{content:"\f66b"}.fa-key:before{content:"\f084"}.fa-keyboard:before{content:"\f11c"}.fa-khanda:before{content:"\f66d"}.fa-kip-sign:before{content:"\e1c4"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-kitchen-set:before{content:"\e51a"}.fa-kiwi-bird:before{content:"\f535"}.fa-l:before{content:"\4c"}.fa-land-mine-on:before{content:"\e51b"}.fa-landmark:before{content:"\f66f"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-landmark-flag:before{content:"\e51c"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-file:before{content:"\e51d"}.fa-laptop-medical:before{content:"\f812"}.fa-lari-sign:before{content:"\e1c8"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-lemon:before{content:"\f094"}.fa-less-than:before{content:"\3c"}.fa-less-than-equal:before{content:"\f537"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-lines-leaning:before{content:"\e51e"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-lira-sign:before{content:"\f195"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-location-arrow:before{content:"\f124"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-location-pin-lock:before{content:"\e51f"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-locust:before{content:"\e520"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-m:before{content:"\4d"}.fa-magnet:before{content:"\f076"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-manat-sign:before{content:"\e1d5"}.fa-map:before{content:"\f279"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-pin:before{content:"\f276"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-and-venus:before{content:"\f224"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-mask:before{content:"\f6fa"}.fa-mask-face:before{content:"\e1d7"}.fa-mask-ventilator:before{content:"\e524"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-mattress-pillow:before{content:"\e525"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-medal:before{content:"\f5a2"}.fa-memory:before{content:"\f538"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-mill-sign:before{content:"\e1ed"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-mitten:before{content:"\f7b5"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-mobile-button:before{content:"\f10b"}.fa-mobile-retro:before{content:"\e527"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-money-bills:before{content:"\e1f3"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-mosquito-net:before{content:"\e52c"}.fa-motorcycle:before{content:"\f21c"}.fa-mound:before{content:"\e52d"}.fa-mountain:before{content:"\f6fc"}.fa-mountain-city:before{content:"\e52e"}.fa-mountain-sun:before{content:"\e52f"}.fa-mug-hot:before{content:"\f7b6"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-music:before{content:"\f001"}.fa-n:before{content:"\4e"}.fa-naira-sign:before{content:"\e1f6"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-not-equal:before{content:"\f53e"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-notes-medical:before{content:"\f481"}.fa-o:before{content:"\4f"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-oil-can:before{content:"\f613"}.fa-oil-well:before{content:"\e532"}.fa-om:before{content:"\f679"}.fa-otter:before{content:"\f700"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-p:before{content:"\50"}.fa-pager:before{content:"\f815"}.fa-paint-roller:before{content:"\f5aa"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-palette:before{content:"\f53f"}.fa-pallet:before{content:"\f482"}.fa-panorama:before{content:"\e209"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-passport:before{content:"\f5ab"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-pause:before{content:"\f04c"}.fa-paw:before{content:"\f1b0"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-people-group:before{content:"\e533"}.fa-people-line:before{content:"\e534"}.fa-people-pulling:before{content:"\e535"}.fa-people-robbery:before{content:"\e536"}.fa-people-roof:before{content:"\e537"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-person-booth:before{content:"\f756"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-person-burst:before{content:"\e53b"}.fa-person-cane:before{content:"\e53c"}.fa-person-chalkboard:before{content:"\e53d"}.fa-person-circle-check:before{content:"\e53e"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-person-circle-minus:before{content:"\e540"}.fa-person-circle-plus:before{content:"\e541"}.fa-person-circle-question:before{content:"\e542"}.fa-person-circle-xmark:before{content:"\e543"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-person-dress-burst:before{content:"\e544"}.fa-person-drowning:before{content:"\e545"}.fa-person-falling:before{content:"\e546"}.fa-person-falling-burst:before{content:"\e547"}.fa-person-half-dress:before{content:"\e548"}.fa-person-harassing:before{content:"\e549"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-person-military-pointing:before{content:"\e54a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-person-military-to-person:before{content:"\e54c"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-person-pregnant:before{content:"\e31e"}.fa-person-rays:before{content:"\e54d"}.fa-person-rifle:before{content:"\e54e"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-person-shelter:before{content:"\e54f"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-person-through-window:before{content:"\e433"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-person-walking-luggage:before{content:"\e554"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-peseta-sign:before{content:"\e221"}.fa-peso-sign:before{content:"\e222"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-circle-check:before{content:"\e555"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-lock:before{content:"\e558"}.fa-plane-slash:before{content:"\e069"}.fa-plane-up:before{content:"\e22d"}.fa-plant-wilt:before{content:"\e43b"}.fa-plate-wheat:before{content:"\e55a"}.fa-play:before{content:"\f04b"}.fa-plug:before{content:"\f1e6"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-plug-circle-check:before{content:"\e55c"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-plus-minus:before{content:"\e43c"}.fa-podcast:before{content:"\f2ce"}.fa-poo:before{content:"\f2fe"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-power-off:before{content:"\f011"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-puzzle-piece:before{content:"\f12e"}.fa-q:before{content:"\51"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\3f"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-r:before{content:"\52"}.fa-radiation:before{content:"\f7b9"}.fa-radio:before{content:"\f8d7"}.fa-rainbow:before{content:"\f75b"}.fa-ranking-star:before{content:"\e561"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-recycle:before{content:"\f1b8"}.fa-registered:before{content:"\f25d"}.fa-repeat:before{content:"\f363"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-republican:before{content:"\f75e"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-ribbon:before{content:"\f4d6"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-road-barrier:before{content:"\e562"}.fa-road-bridge:before{content:"\e563"}.fa-road-circle-check:before{content:"\e564"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-road-circle-xmark:before{content:"\e566"}.fa-road-lock:before{content:"\e567"}.fa-road-spikes:before{content:"\e568"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-route:before{content:"\f4d7"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-rug:before{content:"\e569"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-rupiah-sign:before{content:"\e23d"}.fa-s:before{content:"\53"}.fa-sack-dollar:before{content:"\f81d"}.fa-sack-xmark:before{content:"\e56a"}.fa-sailboat:before{content:"\e445"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-school:before{content:"\f549"}.fa-school-circle-check:before{content:"\e56b"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-school-flag:before{content:"\e56e"}.fa-school-lock:before{content:"\e56f"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-screwdriver:before{content:"\f54a"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-scroll:before{content:"\f70e"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-sd-card:before{content:"\f7c2"}.fa-section:before{content:"\e447"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-server:before{content:"\f233"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-arrow-turn-right:before,.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-sheet-plastic:before{content:"\e571"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-shield-cat:before{content:"\e572"}.fa-shield-dog:before{content:"\e573"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-shield-heart:before{content:"\e574"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-shoe-prints:before{content:"\f54b"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-shop-lock:before{content:"\e4a5"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-shower:before{content:"\f2cc"}.fa-shrimp:before{content:"\e448"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-sim-card:before{content:"\f7c4"}.fa-sink:before{content:"\e06d"}.fa-sitemap:before{content:"\f0e8"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-spa:before{content:"\f5bb"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-spray-can:before{content:"\f5bd"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-square:before{content:"\f0c8"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-square-full:before{content:"\f45c"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-square-nfi:before{content:"\e576"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-square-person-confined:before{content:"\e577"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-square-virus:before{content:"\e578"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-stairs:before{content:"\e289"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-stethoscope:before{content:"\f0f1"}.fa-stop:before{content:"\f04d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-slash:before{content:"\e071"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stroopwafel:before{content:"\f551"}.fa-subscript:before{content:"\f12c"}.fa-suitcase:before{content:"\f0f2"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-superscript:before{content:"\f12b"}.fa-swatchbook:before{content:"\f5c3"}.fa-synagogue:before{content:"\f69b"}.fa-syringe:before{content:"\f48e"}.fa-t:before{content:"\54"}.fa-table:before{content:"\f0ce"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-tablet-button:before{content:"\f10a"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tarp:before{content:"\e57b"}.fa-tarp-droplet:before{content:"\e57c"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-tent:before{content:"\e57d"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tent-arrows-down:before{content:"\e581"}.fa-tents:before{content:"\e582"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-text-width:before{content:"\f035"}.fa-thermometer:before{content:"\f491"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-ticket:before{content:"\f145"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-timeline:before{content:"\e29c"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toilet-portable:before{content:"\e583"}.fa-toilets-portable:before{content:"\e584"}.fa-toolbox:before{content:"\f552"}.fa-tooth:before{content:"\f5c9"}.fa-torii-gate:before{content:"\f6a1"}.fa-tornado:before{content:"\f76f"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-tower-cell:before{content:"\e585"}.fa-tower-observation:before{content:"\e586"}.fa-tractor:before{content:"\f722"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-train-tram:before,.fa-tram:before{content:"\f7da"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-tree-city:before{content:"\e587"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-trophy:before{content:"\f091"}.fa-trowel:before{content:"\e589"}.fa-trowel-bricks:before{content:"\e58a"}.fa-truck:before{content:"\f0d1"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-truck-droplet:before{content:"\e58c"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-truck-field:before{content:"\e58d"}.fa-truck-field-un:before{content:"\e58e"}.fa-truck-front:before{content:"\e2b7"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-truck-plane:before{content:"\e58f"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-u:before{content:"\55"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-universal-access:before{content:"\f29a"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-upload:before{content:"\f093"}.fa-user:before{content:"\f007"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-clock:before{content:"\f4fd"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-user-graduate:before{content:"\f501"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-user-injured:before{content:"\f728"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-user-lock:before{content:"\f502"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-between-lines:before{content:"\e591"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-users-line:before{content:"\e592"}.fa-users-rays:before{content:"\e593"}.fa-users-rectangle:before{content:"\e594"}.fa-users-slash:before{content:"\e073"}.fa-users-viewfinder:before{content:"\e595"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-v:before{content:"\56"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-vault:before{content:"\e2c5"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-vial:before{content:"\f492"}.fa-vial-circle-check:before{content:"\e596"}.fa-vial-virus:before{content:"\e597"}.fa-vials:before{content:"\f493"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-virus:before{content:"\e074"}.fa-virus-covid:before{content:"\e4a8"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-voicemail:before{content:"\f897"}.fa-volcano:before{content:"\f770"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-volume-off:before{content:"\f026"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-vr-cardboard:before{content:"\f729"}.fa-w:before{content:"\57"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-wallet:before{content:"\f555"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-wand-sparkles:before{content:"\f72b"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-wave-square:before{content:"\f83e"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-wheelchair:before{content:"\f193"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-wind:before{content:"\f72e"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-worm:before{content:"\e599"}.fa-wrench:before{content:"\f0ad"}.fa-x:before{content:"\58"}.fa-x-ray:before{content:"\f497"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-xmarks-lines:before{content:"\e59a"}.fa-y:before{content:"\59"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-z:before{content:"\5a"}.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-family:"Font Awesome 6 Brands";font-weight:400}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-alipay:before{content:"\f642"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-amilia:before{content:"\f36d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-pay:before{content:"\f415"}.fa-artstation:before{content:"\f77a"}.fa-asymmetrik:before{content:"\f372"}.fa-atlassian:before{content:"\f77b"}.fa-audible:before{content:"\f373"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-aws:before{content:"\f375"}.fa-bandcamp:before{content:"\f2d5"}.fa-battle-net:before{content:"\f835"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bilibili:before{content:"\e3d9"}.fa-bimobject:before{content:"\f378"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bootstrap:before{content:"\f836"}.fa-bots:before{content:"\e340"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-buromobelexperte:before{content:"\f37f"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cmplid:before{content:"\e360"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cotton-bureau:before{content:"\f89e"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-critical-role:before{content:"\f6c9"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dhl:before{content:"\f790"}.fa-diaspora:before{content:"\f791"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-draft2digital:before{content:"\f396"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drupal:before{content:"\f1a9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-elementor:before{content:"\f430"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-etsy:before{content:"\f2d7"}.fa-evernote:before{content:"\f839"}.fa-expeditedssl:before{content:"\f23e"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-figma:before{content:"\f799"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-fly:before{content:"\f417"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-fulcrum:before{content:"\f50b"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-gofore:before{content:"\f3a7"}.fa-golang:before{content:"\e40f"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-gulp:before{content:"\f3ae"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hashnode:before{content:"\e499"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-hive:before{content:"\e07f"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-hotjar:before{content:"\f3b1"}.fa-houzz:before{content:"\f27c"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-ideal:before{content:"\e013"}.fa-imdb:before{content:"\f2d8"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\e055"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joomla:before{content:"\f1aa"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaggle:before{content:"\f5fa"}.fa-keybase:before{content:"\f4f5"}.fa-keycdn:before{content:"\f3ba"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-korvue:before{content:"\f42f"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-leanpub:before{content:"\f212"}.fa-less:before{content:"\f41d"}.fa-line:before{content:"\f3c0"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-mailchimp:before{content:"\f59e"}.fa-mandalorian:before{content:"\f50f"}.fa-markdown:before{content:"\f60f"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medapps:before{content:"\f3c6"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-mendeley:before{content:"\f7b3"}.fa-microblog:before{content:"\e01a"}.fa-microsoft:before{content:"\f3ca"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-nfc-directional:before{content:"\e530"}.fa-nfc-symbol:before{content:"\e531"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-old-republic:before{content:"\f510"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-padlet:before{content:"\e4a0"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-palfed:before{content:"\f3d8"}.fa-patreon:before{content:"\f3d9"}.fa-paypal:before{content:"\f1ed"}.fa-perbyte:before{content:"\e083"}.fa-periscope:before{content:"\f3da"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\e01e"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pix:before{content:"\e43a"}.fa-playstation:before{content:"\f3df"}.fa-product-hunt:before{content:"\f288"}.fa-pushed:before{content:"\f3e1"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-r-project:before{content:"\f4f7"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-renren:before{content:"\f18b"}.fa-replyd:before{content:"\f3e6"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-rev:before{content:"\f5b2"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-rust:before{content:"\e07a"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-schlix:before{content:"\f3ea"}.fa-screenpal:before{content:"\e570"}.fa-scribd:before{content:"\f28a"}.fa-searchengin:before{content:"\f3eb"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-servicestack:before{content:"\f3ec"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shopify:before{content:"\e057"}.fa-shopware:before{content:"\f5b5"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sith:before{content:"\f512"}.fa-sitrox:before{content:"\e44a"}.fa-sketch:before{content:"\f7c6"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-slideshare:before{content:"\f1e7"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-square:before{content:"\f2ad"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spotify:before{content:"\f1bc"}.fa-square-font-awesome:before{content:"\f425"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-sticker-mule:before{content:"\f3f7"}.fa-strava:before{content:"\f428"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-superpowers:before{content:"\f2dd"}.fa-supple:before{content:"\f3f9"}.fa-suse:before{content:"\f7d6"}.fa-swift:before{content:"\f8e1"}.fa-symfony:before{content:"\f83d"}.fa-teamspeak:before{content:"\f4f9"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-the-red-yeti:before{content:"\f69d"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-think-peaks:before{content:"\f731"}.fa-tiktok:before{content:"\e07b"}.fa-trade-federation:before{content:"\f513"}.fa-trello:before{content:"\f181"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-uncharted:before{content:"\e084"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-vaadin:before{content:"\f408"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-viber:before{content:"\f409"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-vuejs:before{content:"\f41f"}.fa-watchman-monitoring:before{content:"\e087"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-whmcs:before{content:"\f40d"}.fa-wikipedia-w:before{content:"\f266"}.fa-windows:before{content:"\f17a"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-family:"Font Awesome 6 Free";font-weight:400}:host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-family:"Font Awesome 6 Free";font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f250,u+f252,u+f27a}
{wpf-assets/css/font-awesome/css → assets/css/font-awesome}/index.html RENAMED
File without changes
assets/css/font-awesome/webfonts/fa-brands-400.ttf ADDED
Binary file
assets/css/font-awesome/webfonts/fa-brands-400.woff2 ADDED
Binary file
assets/css/font-awesome/webfonts/fa-regular-400.ttf ADDED
Binary file
assets/css/font-awesome/webfonts/fa-regular-400.woff2 ADDED
Binary file
assets/css/font-awesome/webfonts/fa-solid-900.ttf ADDED
Binary file
assets/css/font-awesome/webfonts/fa-solid-900.woff2 ADDED
Binary file
assets/css/font-awesome/webfonts/fa-v4compatibility.ttf ADDED
Binary file
assets/css/font-awesome/webfonts/fa-v4compatibility.woff2 ADDED
Binary file
{wpf-assets/css/font-awesome → assets/css}/index.html RENAMED
File without changes
{wpf-assets → assets}/css/wpforo-sitemap.xsl RENAMED
File without changes
{wpf-assets → assets}/images/ajax_loading.gif RENAMED
File without changes
assets/images/category-bg.jpg ADDED
Binary file
assets/images/dashboard/addon.png ADDED
Binary file
assets/images/dashboard/akismet.png ADDED
Binary file
assets/images/dashboard/arrow-right.png ADDED
Binary file
assets/images/dashboard/cog.png ADDED
Binary file
assets/images/dashboard/dots.png ADDED
Binary file
{wpf-assets/css/font-awesome/webfonts → assets/images/dashboard}/index.html RENAMED
File without changes
assets/images/dashboard/new.png ADDED
Binary file
assets/images/dashboard/note.png ADDED
Binary file
assets/images/dashboard/ok.png ADDED
Binary file
assets/images/dashboard/wpforo-logo.png ADDED
Binary file
assets/images/dashboard/wpforo.png ADDED
Binary file
{wpf-assets/css → assets/images}/index.html RENAMED
File without changes
{wpf-assets → assets}/images/loading.gif RENAMED
File without changes
{wpf-assets → assets}/images/noavatar.png RENAMED
File without changes
assets/images/profile-bg.jpg ADDED
Binary file
{wpf-assets → assets}/images/sn/aim.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/facebook.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/fb-m.jpg RENAMED
File without changes
{wpf-assets → assets}/images/sn/fb-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/gg-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/gtalk.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/icq.png RENAMED
File without changes
{wpf-assets/images → assets/images/sn}/index.html RENAMED
File without changes
{wpf-assets → assets}/images/sn/lin-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/linkedin.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/location-1.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/location-2.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/location-3.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/location-4.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/location-5.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/location-6.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/msn.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/ok-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/share-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/skype.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/toggle-1.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/toggle-2.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/toggle-3.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/toggle-4.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/tw-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/twitter.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/vk-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/wapp-m.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/whatsapp.png RENAMED
File without changes
{wpf-assets → assets}/images/sn/yahoo.png RENAMED
File without changes
{wpf-assets → assets}/images/wpforo-40.png RENAMED
File without changes
{wpf-assets → assets}/images/wpforo-info.png RENAMED
File without changes
{wpf-assets → assets}/images/wpforo-logo.png RENAMED
File without changes
{wpf-assets/images/sn → assets}/index.html RENAMED
File without changes
{wpf-assets → assets}/js/ajax.js RENAMED
@@ -38,6 +38,54 @@ $wpf(document).ready(function ($) {
38
  wpforo_post_url_fixer(window.location.hash);
39
  };
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  // Like
42
  wpforo_wrap.on('click', '.wpforo-like:not(.wpf-processing)', function () {
43
  wpforo_load_show();
@@ -56,7 +104,7 @@ $wpf(document).ready(function ($) {
56
  if (response.success) {
57
  $this.find('.wpforo-like-ico').removeClass('far').addClass('fas');
58
  $this.find('.wpforo-like-txt').text(' ' + wpforo_phrase('Unlike'));
59
- $this.parents('.wpforo-post').find('.bleft').html(response.data.likers);
60
  $this.removeClass('wpforo-like').addClass('wpforo-unlike');
61
  if( $this.children(".wpf-like-icon").is("[wpf-tooltip]") ) {
62
  $this.children(".wpf-like-icon").attr("wpf-tooltip", wpforo_phrase('Unlike') );
@@ -88,7 +136,7 @@ $wpf(document).ready(function ($) {
88
  if (response.success) {
89
  $this.find('.wpforo-like-ico').removeClass('fas').addClass('far');
90
  $this.find('.wpforo-like-txt').text(' ' + wpforo_phrase('Like'));
91
- $this.parents('.wpforo-post').find('.bleft').html(response.data.likers);
92
  $this.removeClass('wpforo-unlike').addClass('wpforo-like');
93
  if( $this.children(".wpf-like-icon").is("[wpf-tooltip]") ) {
94
  $this.children(".wpf-like-icon").attr("wpf-tooltip", wpforo_phrase('Like') );
@@ -112,17 +160,26 @@ $wpf(document).ready(function ($) {
112
  $this = $(this);
113
  var buttons = $('.wpforo-voteup, .wpforo-votedown', $this.closest('.wpforo-post-voting'));
114
  buttons.addClass('wpf-processing');
 
115
  $.ajax({
116
  type: 'POST',
117
  url: wpforo.ajax_url,
118
  data: {
119
  itemtype: type,
120
- postid: postid,
121
- votestatus: 'up',
122
  action: 'wpforo_vote_ajax'
123
  }
124
  }).done(function (response) {
125
- if( response.success ) $this.parents('.post-wrap').find('.wpfvote-num').text(response.data.votes).fadeIn();
 
 
 
 
 
 
 
 
126
  wpforo_load_hide();
127
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
128
  buttons.removeClass('wpf-processing');
@@ -136,17 +193,26 @@ $wpf(document).ready(function ($) {
136
  $this = $(this);
137
  var buttons = $('.wpforo-voteup, .wpforo-votedown', $this.closest('.wpforo-post-voting'));
138
  buttons.addClass('wpf-processing');
 
139
  $.ajax({
140
  type: 'POST',
141
  url: wpforo.ajax_url,
142
  data: {
143
  itemtype: type,
144
- postid: postid,
145
- votestatus: 'down',
146
  action: 'wpforo_vote_ajax'
147
  }
148
  }).done(function (response) {
149
- if( response.success ) $this.parents('.post-wrap').find('.wpfvote-num').text(response.data.votes).fadeIn();
 
 
 
 
 
 
 
 
150
  wpforo_load_hide();
151
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
152
  buttons.removeClass('wpf-processing');
@@ -703,79 +769,67 @@ $wpf(document).ready(function ($) {
703
 
704
 
705
  // Subscribe
706
- wpforo_wrap.on('click','.wpf-subscribe-forum:not(.wpf-processing), .wpf-subscribe-topic:not(.wpf-processing)', function(){
707
  wpforo_load_show();
708
 
709
  var $this = $(this);
710
  $this.addClass('wpf-processing');
711
 
712
- var type = '';
713
- var clases = $(this).attr('class');
714
-
715
- if( clases.indexOf("wpf-subscribe-forum") > -1 ){
716
- type = 'forum';
717
- }
718
- if( clases.indexOf("wpf-subscribe-topic") > -1 ){
719
- type = 'topic';
720
- }
721
-
722
- var postid_value = $(this).attr('id');
723
- var itemid = postid_value.replace("wpfsubscribe-", "");
724
 
725
  $.ajax({
726
  type: 'POST',
727
  url: wpforo.ajax_url,
728
  data: {
729
- itemid: itemid,
730
- type: type,
731
  status: 'subscribe',
732
  action: 'wpforo_subscribe_ajax'
733
  }
734
  }).done(function (response) {
735
  if( response.success ){
736
- $("#wpfsubscribe-" + itemid).removeClass('wpf-subscribe-' + type).addClass('wpf-unsubscribe-' + type).text(' ' + wpforo_phrase('Unsubscribe'));
737
  }
738
- wpforo_load_hide();
739
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
 
 
740
  $this.removeClass('wpf-processing');
741
  });
742
  });
743
 
744
- wpforo_wrap.on('click','.wpf-unsubscribe-forum:not(.wpf-processing), .wpf-unsubscribe-topic:not(.wpf-processing)', function(){
745
  wpforo_load_show();
746
 
747
  var $this = $(this);
748
  $this.addClass('wpf-processing');
749
 
750
- var type = '';
 
751
  var button_phrase = '';
752
- var clases = $(this).attr('class');
753
- if( clases.indexOf("wpf-unsubscribe-forum") > -1 ){
754
- type = 'forum';
755
  button_phrase = wpforo_ucwords( wpforo_phrase('Subscribe for new topics') );
756
- }
757
- if( clases.indexOf("wpf-unsubscribe-topic") > -1 ){
758
- type = 'topic';
759
  button_phrase = wpforo_ucwords( wpforo_phrase('Subscribe for new replies') );
 
 
760
  }
761
- var postid_value = $(this).attr('id');
762
- var itemid = postid_value.replace("wpfsubscribe-", "");
763
-
764
  $.ajax({
765
  type: 'POST',
766
  url: wpforo.ajax_url,
767
  data: {
768
- itemid: itemid,
769
- type: type,
770
  status: 'unsubscribe',
771
  action: 'wpforo_subscribe_ajax'
772
  }
773
  }).done(function (response) {
774
  if( response.success ){
775
- $("#wpfsubscribe-" + itemid).removeClass('wpf-unsubscribe-' + type).addClass('wpf-subscribe-' + type).text(' ' + button_phrase);
776
  }
777
- wpforo_load_hide();
778
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
 
 
779
  $this.removeClass('wpf-processing');
780
  });
781
  });
@@ -1106,6 +1160,56 @@ $wpf(document).ready(function ($) {
1106
  }
1107
  });
1108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1109
  function wpforo_activate_revision_action_buttons(form){
1110
  var rev_saved = $('.wpforo_revision_saved', form);
1111
  if( rev_saved.is(':visible') ){
@@ -1113,7 +1217,7 @@ $wpf(document).ready(function ($) {
1113
  var save_revision = $('.wpforo_save_revision', form);
1114
  save_revision.show();
1115
 
1116
- if( parseInt(wpforo.revision_options.is_draft_on) && parseInt(wpforo.revision_options.auto_draft_interval) && !save_revision.data('auto_draft') ){
1117
  setInterval(function(){
1118
  save_revision.trigger("click");
1119
  }, wpforo.revision_options.auto_draft_interval);
@@ -1160,9 +1264,9 @@ $wpf(document).ready(function ($) {
1160
  if( f && f.length ){
1161
  var t = $('[type="text"][required]', f);
1162
  if( t.length && t.val().length ){
1163
- wpforo_tinymce_initializeIt('.wp-editor-area');
1164
  }else{
1165
- wpforo_tinymce_initializeIt('.wp-editor-area', true);
1166
  t.trigger("focus");
1167
  }
1168
  }
@@ -1208,6 +1312,397 @@ $wpf(document).ready(function ($) {
1208
  });
1209
  });
1210
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1211
  });
1212
 
1213
  function wpforo_init_phrases(){
38
  wpforo_post_url_fixer(window.location.hash);
39
  };
40
 
41
+ // Reactions
42
+ wpforo_wrap.on('click', '.wpforo-reaction .wpf-react:not(.wpf-processing)', function(){
43
+ var $this = $( this );
44
+ var wrap = $this.closest( '.wpf-reaction-wrap' );
45
+ var type = $this.data( 'type' );
46
+ if( !type.trim() ) type = 'up';
47
+ var postid = $this.closest( '[data-postid]' ).data('postid');
48
+
49
+ $this.addClass( 'wpf-processing' );
50
+ $.ajax({
51
+ type: 'POST',
52
+ data: {
53
+ postid,
54
+ type,
55
+ action: 'wpforo_react',
56
+ }
57
+ }).done(function( response ){
58
+ if( response.success ){
59
+ $( '.reacted-users', wrap.closest( '.wpforo-post' ) ).html( response.data['likers'] );
60
+ wrap.replaceWith( response.data['like_button'] );
61
+ }
62
+ }).always(function(){
63
+ $this.removeClass( 'wpf-processing' );
64
+ });
65
+ });
66
+
67
+ wpforo_wrap.on('click', '.wpforo-reaction .wpf-unreact:not(.wpf-processing)', function(){
68
+ var $this = $( this );
69
+ var wrap = $this.closest( '.wpf-reaction-wrap' );
70
+ var postid = $this.closest( '[data-postid]' ).data('postid');
71
+
72
+ $this.addClass( 'wpf-processing' );
73
+ $.ajax({
74
+ type: 'POST',
75
+ data: {
76
+ postid,
77
+ action: 'wpforo_unreact',
78
+ }
79
+ }).done(function( response ){
80
+ if( response.success ){
81
+ $( '.reacted-users', wrap.closest( '.wpforo-post' ) ).html( response.data['likers'] );
82
+ wrap.replaceWith( response.data['like_button'] );
83
+ }
84
+ }).always(function(){
85
+ $this.removeClass( 'wpf-processing' );
86
+ });
87
+ });
88
+
89
  // Like
90
  wpforo_wrap.on('click', '.wpforo-like:not(.wpf-processing)', function () {
91
  wpforo_load_show();
104
  if (response.success) {
105
  $this.find('.wpforo-like-ico').removeClass('far').addClass('fas');
106
  $this.find('.wpforo-like-txt').text(' ' + wpforo_phrase('Unlike'));
107
+ $this.parents('.wpforo-post').find('.reacted-users').html(response.data.likers);
108
  $this.removeClass('wpforo-like').addClass('wpforo-unlike');
109
  if( $this.children(".wpf-like-icon").is("[wpf-tooltip]") ) {
110
  $this.children(".wpf-like-icon").attr("wpf-tooltip", wpforo_phrase('Unlike') );
136
  if (response.success) {
137
  $this.find('.wpforo-like-ico').removeClass('fas').addClass('far');
138
  $this.find('.wpforo-like-txt').text(' ' + wpforo_phrase('Like'));
139
+ $this.parents('.wpforo-post').find('.reacted-users').html(response.data.likers);
140
  $this.removeClass('wpforo-unlike').addClass('wpforo-like');
141
  if( $this.children(".wpf-like-icon").is("[wpf-tooltip]") ) {
142
  $this.children(".wpf-like-icon").attr("wpf-tooltip", wpforo_phrase('Like') );
160
  $this = $(this);
161
  var buttons = $('.wpforo-voteup, .wpforo-votedown', $this.closest('.wpforo-post-voting'));
162
  buttons.addClass('wpf-processing');
163
+ var votestatus = ($this.hasClass('wpf-vote-active') ? 'clear' : 'up');
164
  $.ajax({
165
  type: 'POST',
166
  url: wpforo.ajax_url,
167
  data: {
168
  itemtype: type,
169
+ postid,
170
+ votestatus,
171
  action: 'wpforo_vote_ajax'
172
  }
173
  }).done(function (response) {
174
+ if( response.success ) {
175
+ $this.parents('.post-wrap').find('.wpfvote-num').text(response.data.votes).fadeIn();
176
+ var buttons_wrap = $this.closest( '.wpforo-post-voting' );
177
+ if( buttons_wrap.length ){
178
+ var vote_active_buttons = $( '.wpf-vote-active', buttons_wrap );
179
+ if( vote_active_buttons.length ) vote_active_buttons.removeClass('wpf-vote-active');
180
+ }
181
+ if( votestatus !== 'clear' ) $this.addClass('wpf-vote-active');
182
+ }
183
  wpforo_load_hide();
184
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
185
  buttons.removeClass('wpf-processing');
193
  $this = $(this);
194
  var buttons = $('.wpforo-voteup, .wpforo-votedown', $this.closest('.wpforo-post-voting'));
195
  buttons.addClass('wpf-processing');
196
+ var votestatus = ($this.hasClass('wpf-vote-active') ? 'clear' : 'down');
197
  $.ajax({
198
  type: 'POST',
199
  url: wpforo.ajax_url,
200
  data: {
201
  itemtype: type,
202
+ postid,
203
+ votestatus,
204
  action: 'wpforo_vote_ajax'
205
  }
206
  }).done(function (response) {
207
+ if( response.success ) {
208
+ $this.parents('.post-wrap').find('.wpfvote-num').text(response.data.votes).fadeIn();
209
+ var buttons_wrap = $this.closest( '.wpforo-post-voting' );
210
+ if( buttons_wrap.length ){
211
+ var vote_active_buttons = $( '.wpf-vote-active', buttons_wrap );
212
+ if( vote_active_buttons.length ) vote_active_buttons.removeClass('wpf-vote-active');
213
+ }
214
+ if( votestatus !== 'clear' ) $this.addClass('wpf-vote-active');
215
+ }
216
  wpforo_load_hide();
217
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
218
  buttons.removeClass('wpf-processing');
769
 
770
 
771
  // Subscribe
772
+ wpforo_wrap.on('click','.wpf-subscribe-forum:not(.wpf-processing), .wpf-subscribe-topic:not(.wpf-processing), .wpf-subscribe-user:not(.wpf-processing)', function(){
773
  wpforo_load_show();
774
 
775
  var $this = $(this);
776
  $this.addClass('wpf-processing');
777
 
778
+ var type = $this.data('type');
779
+ var itemid = $this.data('itemid');
 
 
 
 
 
 
 
 
 
 
780
 
781
  $.ajax({
782
  type: 'POST',
783
  url: wpforo.ajax_url,
784
  data: {
785
+ itemid,
786
+ type,
787
  status: 'subscribe',
788
  action: 'wpforo_subscribe_ajax'
789
  }
790
  }).done(function (response) {
791
  if( response.success ){
792
+ $this.removeClass('wpf-subscribe-' + type).addClass('wpf-unsubscribe-' + type).text(' ' + wpforo_phrase('Unsubscribe'));
793
  }
 
794
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
795
+ }).always(function(){
796
+ wpforo_load_hide();
797
  $this.removeClass('wpf-processing');
798
  });
799
  });
800
 
801
+ wpforo_wrap.on('click','.wpf-unsubscribe-forum:not(.wpf-processing), .wpf-unsubscribe-topic:not(.wpf-processing), .wpf-unsubscribe-user:not(.wpf-processing)', function(){
802
  wpforo_load_show();
803
 
804
  var $this = $(this);
805
  $this.addClass('wpf-processing');
806
 
807
+ var type = $this.data('type');
808
+ var itemid = $this.data('itemid');
809
  var button_phrase = '';
810
+ if( type === 'forum' ){
 
 
811
  button_phrase = wpforo_ucwords( wpforo_phrase('Subscribe for new topics') );
812
+ }else if( type === 'topic' ){
 
 
813
  button_phrase = wpforo_ucwords( wpforo_phrase('Subscribe for new replies') );
814
+ }else if( type === 'user' ){
815
+ button_phrase = wpforo_ucwords( wpforo_phrase('Subscribe for new posts') );
816
  }
 
 
 
817
  $.ajax({
818
  type: 'POST',
819
  url: wpforo.ajax_url,
820
  data: {
821
+ itemid,
822
+ type,
823
  status: 'unsubscribe',
824
  action: 'wpforo_subscribe_ajax'
825
  }
826
  }).done(function (response) {
827
  if( response.success ){
828
+ $this.removeClass('wpf-unsubscribe-' + type).addClass('wpf-subscribe-' + type).text(' ' + button_phrase);
829
  }
 
830
  wpforo_notice_show(response.data.notice, (response.success ? 'success' : 'error'));
831
+ }).always(function(){
832
+ wpforo_load_hide();
833
  $this.removeClass('wpf-processing');
834
  });
835
  });
1160
  }
1161
  });
1162
 
1163
+ wpforo_wrap.on('click', '.wpforo-user-actions .wpf-ab-mute_mention:not(.wpf-processing)', function(){
1164
+ var $this = $(this);
1165
+ var currentstate = parseInt( $this.data('currentstate') );
1166
+ if( isNaN( currentstate ) ) currentstate = 0;
1167
+ $this.addClass('wpf-processing');
1168
+ wpforo_load_show();
1169
+ $.ajax({
1170
+ type: 'POST',
1171
+ data: {
1172
+ currentstate,
1173
+ action: 'wpforo_mute_mentions'
1174
+ }
1175
+ }).done(function (response) {
1176
+ if (response.success) {
1177
+ $( '> i', $this ).replaceWith(response.data['ico']);
1178
+ wpforo_setAttr( $this, 'data-currentstate', response.data['currentstate'] );
1179
+ var label = $this.data( ( response.data['currentstate'] ? 'active_label' : 'inactive_label' ) )
1180
+ wpforo_setAttr( $this, 'wpf-tooltip', label );
1181
+ }
1182
+ }).always(function(){
1183
+ wpforo_load_hide();
1184
+ $this.removeClass('wpf-processing');
1185
+ });
1186
+ });
1187
+
1188
+ wpforo_wrap.on('click', '.wpforo-user-actions .wpf-ab-ban:not(.wpf-processing)', function(){
1189
+ var $this = $(this);
1190
+ var currentstate = parseInt( $this.data('currentstate') );
1191
+ if( isNaN( currentstate ) ) currentstate = 1;
1192
+ $this.addClass('wpf-processing');
1193
+ wpforo_load_show();
1194
+ $.ajax({
1195
+ type: 'POST',
1196
+ data: {
1197
+ currentstate,
1198
+ action: 'wpforo_user_ban'
1199
+ }
1200
+ }).done(function(response){
1201
+ if (response.success) {
1202
+ wpforo_setAttr( $this, 'data-currentstate', response.data['currentstate'] );
1203
+ var label = $this.data( ( response.data['currentstate'] ? 'active_label' : 'inactive_label' ) )
1204
+ wpforo_setAttr( $this, 'wpf-tooltip', label );
1205
+ }
1206
+ wpforo_notice_show( response.data['notice'] );
1207
+ }).always(function(){
1208
+ wpforo_load_hide();
1209
+ $this.removeClass('wpf-processing');
1210
+ });
1211
+ });
1212
+
1213
  function wpforo_activate_revision_action_buttons(form){
1214
  var rev_saved = $('.wpforo_revision_saved', form);
1215
  if( rev_saved.is(':visible') ){
1217
  var save_revision = $('.wpforo_save_revision', form);
1218
  save_revision.show();
1219
 
1220
+ if( wpforo.revision_options.is_draft_on && parseInt(wpforo.revision_options.auto_draft_interval) && !save_revision.data('auto_draft') ){
1221
  setInterval(function(){
1222
  save_revision.trigger("click");
1223
  }, wpforo.revision_options.auto_draft_interval);
1264
  if( f && f.length ){
1265
  var t = $('[type="text"][required]', f);
1266
  if( t.length && t.val().length ){
1267
+ wpforo_tinymce_initializeIt('[data-wpforoeditor="tinymce"]');
1268
  }else{
1269
+ wpforo_tinymce_initializeIt('[data-wpforoeditor="tinymce"]', true);
1270
  t.trigger("focus");
1271
  }
1272
  }
1312
  });
1313
  });
1314
  });
1315
+
1316
+ /* -- #################################################### -- */
1317
+ /* image adjust and upload from frontend */
1318
+
1319
+ function processfile( file, max_width, max_height, quality, callback ) {
1320
+ if( !( /image/i ).test( file.type ) ) {
1321
+ alert( "File "+ file.name +" is not an image." );
1322
+ return false;
1323
+ }
1324
+
1325
+ // read the files
1326
+ var reader = new FileReader();
1327
+ reader.readAsArrayBuffer(file);
1328
+
1329
+ reader.onload = function (event) {
1330
+ // blob stuff
1331
+ var blob = new Blob([event.target.result]); // create blob...
1332
+ window.URL = window.URL || window.webkitURL;
1333
+ var blobURL = window.URL.createObjectURL(blob); // and get its URL
1334
+
1335
+ // helper Image object
1336
+ var image = new Image();
1337
+ image.src = blobURL;
1338
+ //preview.appendChild(image); // preview commented out, I am using the canvas instead
1339
+ image.onload = function() {
1340
+ // have to wait till it's loaded
1341
+ var resized = resizeMe(image, max_width, max_height, quality); // send it to canvas
1342
+ callback( resized );
1343
+ }
1344
+ };
1345
+ }
1346
+
1347
+ // === RESIZE ====
1348
+ function resizeMe( img, max_width, max_height, quality ) {
1349
+ var canvas = document.createElement('canvas');
1350
+ var width = img.width;
1351
+ var height = img.height;
1352
+
1353
+ // calculate the width and height, constraining the proportions
1354
+ if(width > height) {
1355
+ if (width > max_width) {
1356
+ //height *= max_width / width;
1357
+ height = Math.round(height *= max_width / width);
1358
+ width = max_width;
1359
+ }
1360
+ } else {
1361
+ if (height > max_height) {
1362
+ //width *= max_height / height;
1363
+ width = Math.round(width *= max_height / height);
1364
+ height = max_height;
1365
+ }
1366
+ }
1367
+
1368
+ // resize the canvas and draw the image data into it
1369
+ canvas.width = width;
1370
+ canvas.height = height;
1371
+ var ctx = canvas.getContext("2d");
1372
+ ctx.drawImage(img, 0, 0, width, height);
1373
+
1374
+ return canvas.toDataURL("image/jpeg", quality); // get the data from canvas as 70% JPG (can be also PNG, etc.)
1375
+ }
1376
+
1377
+ function OpenFileDialog (accept, callback) {
1378
+ // this function must be called from a user
1379
+ // activation event (ie an onclick event)
1380
+
1381
+ // Create an input element
1382
+ var inputElement = document.createElement("input");
1383
+
1384
+ // Set its type to file
1385
+ inputElement.type = "file";
1386
+
1387
+ // Set accept to the file types you want the user to select.
1388
+ // Include both the file extension and the mime type
1389
+ inputElement.accept = accept;
1390
+
1391
+ // set onchange event to call callback when user has selected file
1392
+ inputElement.addEventListener("change", callback)
1393
+
1394
+ // dispatch a click event to open the file dialog
1395
+ inputElement.dispatchEvent(new MouseEvent("click"));
1396
+ }
1397
+
1398
+ wpforo_wrap.on( 'click', '.wpforo-profile .wpforo-profile-head .wpf-edit-cover:not(.wpf-processing)', function(){
1399
+ if ( !( window.File && window.FileReader && window.FileList && window.Blob ) ) {
1400
+ alert('The File APIs are not fully supported in this browser.');
1401
+ return false;
1402
+ }
1403
+ var that = $( this );
1404
+ OpenFileDialog('image/*', function(){
1405
+ if( this.files.length ) {
1406
+ that.addClass( 'wpf-processing' );
1407
+ wpforo_load_show('Uploading...');
1408
+ processfile(this.files[0], 1120, 460, 0.7, function (imageblob) {
1409
+ $wpf.ajax({
1410
+ type: 'POST',
1411
+ data: {
1412
+ image_blob: imageblob,
1413
+ action: 'wpforo_profile_cover_upload'
1414
+ }
1415
+ }).done(function(){
1416
+ that.closest('.wpforo-profile-head').css('background-image', 'url(\'' + imageblob + '\')');
1417
+ }).always(function(){
1418
+ that.removeClass('wpf-processing');
1419
+ wpforo_load_hide();
1420
+ });
1421
+ })
1422
+ }
1423
+ });
1424
+ });
1425
+
1426
+ wpforo_wrap.on( 'click', '.wpforo-profile .wpforo-follow-user:not(.wpf-processing)', function(){
1427
+ var that = $( this );
1428
+ var userid = parseInt( that.data('userid') );
1429
+ var stat = !parseInt( that.data('stat') );
1430
+ that.addClass( 'wpf-processing' );
1431
+ wpforo_load_show();
1432
+ $wpf.ajax({
1433
+ type: 'POST',
1434
+ data: {
1435
+ userid,
1436
+ stat: ( stat ? 1 : 0 ),
1437
+ action: 'wpforo_follow_unfollow_user'
1438
+ }
1439
+ }).done(function( r ){
1440
+ if( r.success ){
1441
+ that.data( 'stat', r.data['stat'] );
1442
+ that.prop( 'stat', r.data['stat'] );
1443
+ that.attr( 'stat', r.data['stat'] );
1444
+ $( '.wpforo-follow-user-label', that ).text( r.data['phrase'] );
1445
+ $( '.wpforo-follow-user-followers-count', that.closest( '.wpforo-follow-wrap' ) ).text( r.data['followers_count'] );
1446
+ }
1447
+ wpforo_notice_show( r.data['notice'] );
1448
+ }).always(function(){
1449
+ that.removeClass('wpf-processing');
1450
+ wpforo_load_hide();
1451
+ });
1452
+ });
1453
+
1454
+ wpforo_wrap.on('click', '.wpforo-post-head .wpf-pb-more', function(){
1455
+ var that = $( this );
1456
+ var wrap = that.closest( '.wpforo-post-head' );
1457
+ var topicid = parseInt( that.closest( '.wpforo-topic-head-wrap[data-topicid]' ).data('topicid') );
1458
+
1459
+ if( ! isNaN( topicid ) && topicid ){
1460
+
1461
+ that.find('span > svg').toggle();
1462
+
1463
+ var more = $( '.wpf-topic-more-info', wrap );
1464
+ var already_loaded = !! more.html().trim();
1465
+ if( ! ( ! already_loaded && more.is( ':visible' ) ) ) more.toggle();
1466
+
1467
+ if( ! already_loaded ){
1468
+ more.addClass( 'wpforo-section-loading' );
1469
+
1470
+ $wpf.ajax({
1471
+ type: 'POST',
1472
+ data: {
1473
+ topicid,
1474
+ action: 'wpforo_get_topic_head_more_info'
1475
+ }
1476
+ }).done(function( r ){
1477
+ if( r.success ){
1478
+ more.html( r.data['html'] );
1479
+ }
1480
+ }).always(function(){
1481
+ more.removeClass('wpforo-section-loading');
1482
+ });
1483
+ }
1484
+
1485
+ var ico = $( 'i.fa-chevron-up, i.fa-chevron-down', that);
1486
+ var label = $( 'span', that );
1487
+ if( more.is( ':visible' ) ){
1488
+ ico.removeClass( 'fa-chevron-down' ).addClass( 'fa-chevron-up' );
1489
+ }else{
1490
+ ico.removeClass( 'fa-chevron-up' ).addClass( 'fa-chevron-down' );
1491
+ }
1492
+ }
1493
+ });
1494
+
1495
+ var topic_head_more_info = $( '.wpforo-topic-head-wrap .wpf-topic-more-info', wpforo_wrap );
1496
+ if( topic_head_more_info.is( ':visible' ) && !topic_head_more_info.html().trim() ) {
1497
+ $( '.wpforo-post-head .wpf-pb-more', wpforo_wrap ).trigger( 'click' );
1498
+ }
1499
+
1500
+ wpforo_wrap.on('click', '.wpf-tmi-overview .wpf-action.wpf-topic-overview-load-more:not(.wpf-processing)', function(){
1501
+ var that = $( this );
1502
+ var wrap = that.closest( '.wpf-tmi-overview' );
1503
+ var tree = $( '.wpf-topic-overview-tree', wrap );
1504
+ var nomore = parseInt( tree.data( 'nomore' ) );
1505
+ if( ! nomore ){
1506
+ var topicid = parseInt( that.closest( '.wpforo-topic-head-wrap[data-topicid]' ).data('topicid') );
1507
+ if( ! isNaN( topicid ) ){
1508
+ that.addClass( 'wpf-processing' );
1509
+ wpforo_load_show();
1510
+
1511
+ var chunksize = parseInt( tree.data( 'chunksize' ) );
1512
+ var offset = parseInt( tree.data( 'offset' ) );
1513
+ if( isNaN( chunksize ) ) chunksize = 5;
1514
+ if( isNaN( offset ) ) offset = 0;
1515
+ offset = offset + chunksize;
1516
+
1517
+ $wpf.ajax({
1518
+ type : 'POST',
1519
+ data : {
1520
+ topicid,
1521
+ chunksize,
1522
+ offset,
1523
+ action: 'wpforo_get_topic_overview_chunk'
1524
+ }
1525
+ }).done(function( r ){
1526
+ if( r.success ){
1527
+ var nomore = r.data['nomore'] ? 1 : 0;
1528
+ tree.append( r.data['html'] );
1529
+
1530
+ wpforo_setAttr( tree, 'data-offset', offset );
1531
+ wpforo_setAttr( tree, 'data-chunksize', chunksize );
1532
+ wpforo_setAttr( tree, 'data-nomore', nomore );
1533
+ }
1534
+ }).always(function(){
1535
+ that.removeClass('wpf-processing');
1536
+ wpforo_load_hide();
1537
+ });
1538
+ }
1539
+ }
1540
+ });
1541
+
1542
+ wpforo_wrap.on( 'click', '.wpf-tmi-overview .wpf-topic-overview-tree .wpf-tmi-item .wpf-link.wpf-tmi-item-body-excerpt:not(.wpf-processing)', function(){
1543
+ var $this = $( this );
1544
+ var postid = parseInt( $this.data( 'postid' ) );
1545
+ if( !isNaN( postid ) ){
1546
+ if( !wpforo['overview'] ) wpforo['overview'] = [];
1547
+ var overview = wpforo['overview'][postid];
1548
+ if( !overview ){
1549
+ $this.addClass( 'wpf-processing' );
1550
+ wpforo_load_show();
1551
+ $.ajax({
1552
+ type: 'POST',
1553
+ data: {
1554
+ postid,
1555
+ action: 'wpforo_get_overview'
1556
+ }
1557
+ }).done(function( r ){
1558
+ if( r.success ){
1559
+ wpforo['overview'][postid] = r.data;
1560
+ wpforo_dialog_show( r.data['title'], r.data['content'], '50%', '350px' );
1561
+ }
1562
+ }).always(function(){
1563
+ $this.removeClass( 'wpf-processing' );
1564
+ wpforo_load_hide();
1565
+ });
1566
+ }else{
1567
+ wpforo_dialog_show( overview['title'], overview['content'], '50%', '350px' );
1568
+ }
1569
+ }
1570
+ } );
1571
+
1572
+ wpforo_wrap.on( 'click', '.wpf-action.wpforo-bookmark:not(.wpf-processing), .wpf-action.wpforo-unbookmark:not(.wpf-processing)', {}, function(){
1573
+ var $this = $( this );
1574
+ var wrap = $this.closest( '.post-wrap[data-postid], .reply-wrap[data-postid], .comment-wrap[data-postid]' );
1575
+ if( wrap.length ){
1576
+ var postid = parseInt( wrap.data( 'postid' ) );
1577
+ if( ! isNaN( postid ) ){
1578
+ $this.addClass( 'wpf-processing' );
1579
+ $.ajax( {
1580
+ type: 'POST',
1581
+ data: {
1582
+ postid,
1583
+ action: ( $this.hasClass( 'wpforo-bookmark' ) ? 'wpforo_bookmark' : 'wpforo_unbookmark' ),
1584
+ }
1585
+ } ).done( function( r ){
1586
+ if( r.success ) $this.replaceWith( r.data['button'] );
1587
+ } ).always( function(){
1588
+ $this.removeClass( 'wpf-processing' );
1589
+ } );
1590
+ }
1591
+ }
1592
+ } );
1593
+
1594
+ wpforo_wrap.on( 'click', '.sbn-action .wpf-sbn-unsbscrb:not(.wpf-processing)', {}, function(){
1595
+ var $this = $( this );
1596
+ var wrap = $this.closest( '.wpforo-sb' );
1597
+ var key = $this.data( 'key' );
1598
+ var boardid = $this.data( 'boardid' );
1599
+ if( key ){
1600
+ $this.addClass( 'wpf-processing' );
1601
+ $.ajax({
1602
+ type: 'POST',
1603
+ data: {
1604
+ key,
1605
+ boardid,
1606
+ action: 'wpforo_unsubscribe',
1607
+ }
1608
+ }).done(function( r ){
1609
+ if( r.success ) wrap.remove();
1610
+ wpforo_notice_show( r.data['notice'] );
1611
+ setTimeout( function(){
1612
+ wpforo_notice_hide();
1613
+ }, 3000 );
1614
+ }).always(function(){
1615
+ $this.removeClass( 'wpf-processing' );
1616
+ });
1617
+ }
1618
+ } );
1619
+
1620
+ function action_get_member_template( $this, wrap, href ){
1621
+ if( $this && $this.length && wrap && wrap.length && href ){
1622
+ $this.addClass( 'wpf-processing' );
1623
+ wpforo_load_show();
1624
+ $.ajax({
1625
+ type: 'POST',
1626
+ data: {
1627
+ href,
1628
+ 'action': 'wpforo_get_member_template'
1629
+ }
1630
+ }).done(function( r ){
1631
+ if( r.success ){
1632
+ $this.siblings().removeClass( 'wpf-active' );
1633
+ $this.addClass( 'wpf-active' );
1634
+ wrap.html( r.data['html'] );
1635
+ }
1636
+ }).always(function(){
1637
+ $this.removeClass( 'wpf-processing' );
1638
+ wpforo_load_hide();
1639
+ });
1640
+ }
1641
+ }
1642
+
1643
+ wpforo_wrap.on('click', '.wpf-member-template-link.wpf-ajax-link:not(.wpf-processing)', {}, function(e){
1644
+ e.preventDefault();
1645
+ var $this = $( this );
1646
+ var wrap = $this.closest( '.wpf-profile-body' );
1647
+ var href = e.target.href;
1648
+ action_get_member_template( $this, wrap, href );
1649
+ });
1650
+
1651
+ wpforo_wrap.on('click', '.wpf-member-template-link.wpf-ajax-link .wpf-navi .wpf-navi-dropdown', {}, function(){
1652
+ this.onchange = false;
1653
+ });
1654
+
1655
+ wpforo_wrap.on( 'change', '.wpf-member-template-link.wpf-ajax-link .wpf-navi .wpf-navi-dropdown', {}, function( e ){
1656
+ var $this = $( this ).closest( '.wpf-member-template-link.wpf-ajax-link' );
1657
+ var wrap = $this.closest( '.wpf-profile-body' );
1658
+ action_get_member_template( $this, wrap, e.target.value );
1659
+ });
1660
+
1661
+ let ts_input_old_value = '';
1662
+ let ts_request;
1663
+ wpforo_wrap.on('input propertychange', '.wpf-topic-create form .wpf-field-name-title input[type="text"]', {}, function(){
1664
+ if( ts_request && ts_request.readyState !== 4 ) ts_request.abort();
1665
+ setTimeout( () => {
1666
+ const $this = $( this );
1667
+ let input_value = $this.val().trim();
1668
+ if( input_value.length && ts_input_old_value !== input_value ){
1669
+ wpforo_load_show();
1670
+ ts_request = $.ajax({
1671
+ type: 'POST',
1672
+ data: {
1673
+ title: input_value,
1674
+ action: 'wpforo_search_existed_topics',
1675
+ },
1676
+ }).done(function(r){
1677
+ const tlist = document.createElement('div');
1678
+ tlist.classList.add( 'wpf-suggested-topics-list' );
1679
+ $this.siblings( '.wpf-suggested-topics-list' ).remove();
1680
+ $this.after( tlist );
1681
+ if( r.success ){
1682
+ tlist.innerHTML = '<div class="wpf-suggested-topics-title">' + wpforo_phrase('Discussions that my already have the information you are looking for') + ' &nbsp;<i class="fa-solid fa-angles-down"></i></div>';
1683
+ r.data.forEach(function( topic ){
1684
+ const node = document.createElement( 'div' );
1685
+ node.classList.add( 'wpf-suggested-topic-node' );
1686
+ node.innerHTML = `<i class="fa-solid fa-caret-right"></i>&nbsp; <a href="${topic['url']}" target="_blank">${topic['title']}</a>`;
1687
+ tlist.append( node );
1688
+ });
1689
+ }
1690
+ }).always(function(){
1691
+ wpforo_load_hide();
1692
+ });
1693
+
1694
+ ts_input_old_value = input_value;
1695
+ }
1696
+ }, 700);
1697
+ wpforo_load_hide();
1698
+ });
1699
+
1700
+ wpforo_wrap.on('focusout', '.wpf-topic-create form .wpf-field-name-title', {}, function(){
1701
+ setTimeout(function(){
1702
+ $( '.wpf-topic-create form .wpf-field-name-title .wpf-suggested-topics-list' ).remove();
1703
+ }, 1000);
1704
+ });
1705
+
1706
  });
1707
 
1708
  function wpforo_init_phrases(){
assets/js/bootstrap-popover.min.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * @copyright &copy; Kartik Visweswaran, Krajee.com, 2014 - 2021
3
+ * @version 1.5.1
4
+ *
5
+ * Bootstrap Popover Extended - Popover with modal behavior, styling enhancements and more.
6
+ *
7
+ * For more JQuery/Bootstrap plugins and demos visit http://plugins.krajee.com
8
+ * For more Yii related demos visit http://demos.krajee.com
9
+ */!function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):t("object"==typeof module&&"object"==typeof module.exports?require("jquery"):window.jQuery)}(function(t){"use strict";t.fn.popoverXBsVersion||(t.fn.popoverXBsVersion=window.bootstrap&&window.bootstrap.Modal&&bootstrap.Modal.VERSION||window.Modal&&window.Modal.VERSION||"3.x.x");var o,e,i;o={NAMESPACE:".popoverX",modalOptions:["keyboard","focus","show"],kvLog:function(t){t="bootstrap-popover-x: "+t,window.console&&window.console.log?window.console.log(t):window.alert(t)},addCss:function(t,o){t.removeClass(o).addClass(o)},handler:function(t,e,i){var r=e+o.NAMESPACE;return t.off(r).on(r,i)},raise:function(t,e,i){var r=e+(void 0===i?".target":i)+o.NAMESPACE;return t.trigger(r)},isBs:function(o){var e=t.trim((t.fn.popoverXBsVersion||"")+"");return o=parseInt(o,10),e?o===parseInt(e.charAt(0),10):4===o},getDataAttr:function(t){return"data-"+(o.isBs(5)?"bs-":"")+t}},e=function(o,e){var i=this;i.options=e,i.$element=t(o),i.init()},i=function(o,e){var i=this;i.options=e,i.$element=t(o),i.$dialog=i.$element,i.init()},e.prototype={constructor:e,init:function(){var e,i,r=this,n=r.$element,a=r.options||{},s=n.attr("href"),p=function(t){o.kvLog("PopoverX initialization skipped! "+t)};if(r.href=s,!n||!n.length)return void p("PopoverX triggering button element could not be found.");if(n.attr("data-toggle","popover-x"),a.target?(n.data("target",a.target),i=t(a.target)):i=t(n.data("target")||s&&s.replace(/.*(?=#[^\s]+$)/,"")),!i.length)return void p("PopoverX dialog element could not be found.");if(r.$dialog=i,i.attr(o.getDataAttr("backdrop"),"false"),o.addCss(i,"popover-x"),!i.data("popover-x")){var l=t.extend(!0,{remote:s&&!/#/.test(s)},i.data(),n.data(),a);l.$target=n,i.popoverX(l)}return e=a.trigger,"string"!=typeof e?void p("Invalid or improper configuration for PopoverX trigger."):(e=e.split(" "),void t.each(e,function(t,o){r.listen(o)}))},listen:function(t){var e,i,r=this,n=r.$element,a=r.$dialog,s=!1,p=r.href;"manual"!==t&&("click"!==t&&"keyup"!==t&&(s=!0),s?(e="hover"===t?"mouseenter":"focusin",i="hover"===t?"mouseleave":"focusout",o.handler(n,e,function(){o.raise(a,e).popoverX("show")}),o.handler(n,i,function(){o.raise(a,i).popoverX("hide")})):o.handler(n,t,function(e){return"keyup"===t?void(a&&27===e.which&&o.raise(a,t).popoverX("hide")):(p&&"click"===t&&e.preventDefault(),o.raise(a,t).popoverX("toggle"),void o.handler(a,"hide",function(){n.focus()}))}))},destroy:function(){var t=this;t.$element.off(o.NAMESPACE),t.$dialog.modal("dispose").off(o.NAMESPACE)}},i.prototype={constructor:i,init:function(){var e=this,i=e.$element,r=e.options,n=r.$container,a=o.isBs(5);n&&n.length&&(e.$body=n),e.$body&&e.$body.length||(e.$body=t(document.body)),t.each(o.modalOptions,function(t,e){r[e]&&i.attr(o.getDataAttr(e),r[e])}),i.attr(o.getDataAttr("backdrop"),"false"),a&&!i.find(".modal-dialog").length&&(t(document.createElement("div")).addClass("modal-dialog").css({margin:0,"pointer-events":"all"}).insertAfter(i).append(i.contents()).appendTo(i),o.addCss(i.find(".arrow"),"popover-arrow")),o.addCss(i,"is-bs"+t.fn.popoverXBsVersion.charAt(0)),e.bodyPadding=e.$body.css("padding"),e.$target=e.options.$target,e.$marker=t(document.createElement("div")).addClass("popover-x-marker").insertAfter(i).hide(),i.find(".popover-footer").length&&o.addCss(i,"has-footer"),e.options.remote&&i.find(".popover-content").on("load",e.options.remote,function(){i.trigger("load.complete.popoverX")}),i.on("click.dismiss"+o.NAMESPACE,'[data-dismiss="popover-x"]',t.proxy(e.hide,e)),r.closePopoverOnBlur&&t(document).on("click",function(o){var e=t(o.target);e.closest('[data-toggle="popover-x"]').length||(e.closest(".popover-x").length?o.stopPropagation():t(".popover-x:visible").popoverX("hide"))});var s,p="hide.bs.modal.popoverX",l=t(".modal");l.length&&(s=l.find("[data-toggle='popover-x']"),s.length&&l.off(p).on(p,function(){s.each(function(){var o=t(this).data("target")||null,e=t(o);e.length&&e.is(":visible")&&e.popoverX("hide")})})),t(window).resize(function(){var t="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0;!t&&i.hasClass("kv-popover-active")&&(e.hide(),setTimeout(function(){e.show(!0)},50))})},getPlacement:function(){var t=this,o=t.getPosition(),e=t.options.placement,i=document.documentElement,r=document.body,n=i.clientWidth,a=i.clientHeight,s=Math.max(r.scrollTop||0,i.scrollTop),p="horizontal"===e,l=Math.max(r.scrollLeft||0,i.scrollLeft),d="vertical"===e,f=Math.max(0,o.left-l),h=Math.max(0,o.top-s),c="auto"===e||p||d,g=window.innerWidth||i||document.body.clientWidth;if(t.options.autoPlaceSmallScreen&&g<t.options.smallScreenWidth&&(c=!0),c)return n/3>f?a/3>h?p?"right right-top":"bottom bottom-left":2*a/3>h?d?a/2>=h?"bottom bottom-left":"top top-left":"right":p?"right right-bottom":"top top-left":2*n/3>f?a/3>h?p?n/2>=f?"right right-top":"left left-top":"bottom":2*a/3>h?p?n/2>=f?"right":"left":a/2>=h?"bottom":"top":p?n/2>=f?"right right-bottom":"left left-bottom":"top":a/3>h?p?"left left-top":"bottom bottom-left":2*a/3>h?d?a/2>=h?"bottom-right":"top-right":"left":p?"left left-bottom":"top top-left";switch(e){case"auto-top":return n/3>f?"top top-left":2*n/3>f?"top":"top top-right";case"auto-bottom":return n/3>f?"bottom bottom-left":2*n/3>f?"bottom":"bottom bottom-right";case"auto-left":return a/3>h?"left left-top":2*a/3>h?"left":"left left-bottom";case"auto-right":return a/3>h?"right right-top":2*a/3>h?"right":"right right-bottom";default:return e}},getPosition:function(){var o=this,e=o.$target,i=e[0].getBoundingClientRect(),r=o.$body,n=r.css("position");if(r.is(document.body)||"static"===n)return t.extend({},e.offset(),{width:e[0].offsetWidth||i.width,height:e[0].offsetHeight||i.height});if("relative"===n)return{top:e.offset().top-r.offset().top,left:e.offset().left-r.offset().left,width:e[0].offsetWidth||i.width,height:e[0].offsetHeight||i.height};var a=r[0].getBoundingClientRect();return{top:i.top-a.top+r.scrollTop(),left:i.left-a.left+r.scrollLeft(),width:i.width,height:i.height}},refreshPosition:function(){var t,e=this,i=e.$element,r=e.getPosition(),n=i[0].offsetWidth,a=i[0].offsetHeight,s=e.getPlacement();switch(s){case"bottom":t={top:r.top+r.height,left:r.left+r.width/2-n/2};break;case"bottom bottom-left":t={top:r.top+r.height,left:r.left};break;case"bottom bottom-right":t={top:r.top+r.height,left:r.left+r.width-n};break;case"top":t={top:r.top-a,left:r.left+r.width/2-n/2};break;case"top top-left":t={top:r.top-a,left:r.left};break;case"top top-right":t={top:r.top-a,left:r.left+r.width-n};break;case"left":t={top:r.top+r.height/2-a/2,left:r.left-n};break;case"left left-top":t={top:r.top,left:r.left-n};break;case"left left-bottom":t={top:r.top+r.height-a,left:r.left-n};break;case"right":t={top:r.top+r.height/2-a/2,left:r.left+r.width};break;case"right right-top":t={top:r.top,left:r.left+r.width};break;case"right right-bottom":t={top:r.top+r.height-a,left:r.left+r.width};break;default:o.kvLog("Invalid popover placement '"+s+"'.")}i.removeClass("bottom top left right bottom-left top-left bottom-right top-right left-bottom left-top right-bottom right-top").css(t).addClass(s+" in")},validateOpenPopovers:function(){var o=this,e=o.$element;o.options.closeOpenPopovers&&o.$body.find(".popover:visible").each(function(){var o=t(this);o.is(e)||o.popoverX("hide")})},hide:function(){var t=this,o=t.$element;t.$body.removeClass("popover-x-body"),o.removeClass("kv-popover-active"),o.modal("hide"),o.insertBefore(t.$marker)},show:function(t){var e=this,i=e.$element;i.addClass("kv-popover-active"),i.css(e.options.dialogCss).appendTo(e.$body),t||e.validateOpenPopovers(),o.addCss(e.$body,"popover-x-body"),i.modal("show"),e.$body.css({padding:e.bodyPadding}),i.css({padding:0}),e.refreshPosition()},toggle:function(){var t=this;t.$element.is(":visible")?t.hide():t.show()},destroy:function(){var t=this;t.$element.modal("dispose").off(o.NAMESPACE)}},t.fn.popoverButton=function(o){var i=this;return i.each(function(){var i=t(this),r=i.data("popover-button"),n=t.extend({},t.fn.popoverButton.defaults,i.data(),"object"==typeof o&&o);r||i.data("popover-button",r=new e(this,n)),"string"==typeof o&&r[o]()})},t.fn.popoverX=function(o){var e=this;return e.each(function(){var e=t(this),r=e.data("popover-x"),n=t.extend({},t.fn.popoverX.defaults,e.data(),"object"==typeof o&&o);n.$target||(r&&r.$target?n.$target=r.$target:n.$target=o.$target||t(o.target)),r||e.data("popover-x",r=new i(this,n)),"string"==typeof o?r[o]():n.show&&r.show(!0)})},t.fn.popoverButton.defaults={trigger:"click keyup"},t.fn.popoverX.defaults=t.extend(!0,{},{placement:"auto",dialogCss:{top:0,left:0,display:"block","z-index":1065},autoPlaceSmallScreen:!0,smallScreenWidth:640,closeOpenPopovers:!0,closePopoverOnBlur:!0,keyboard:!0,focus:!0}),t.fn.popoverButton.Constructor=e,t.fn.popoverX.Constructor=i,t(document).ready(function(){var o=t("[data-toggle='popover-x']");o.length&&o.popoverButton()})});
{wpf-assets → assets}/js/frontend.js RENAMED
@@ -68,48 +68,47 @@ function wpforo_trigger_custom_event(target, name, detail) {
68
 
69
  function wpforo_tinymce_initializeIt(selector, do_not_focus) {
70
  if( wpforo_editor.is_tinymce_loaded() ){
71
- tinymce.init({
72
- relative_urls : false,
73
- remove_script_host : false,
74
- convert_urls : false,
75
- keep_styles : false,
76
- entities: "38,amp,60,lt,62,gt",
77
- entity_encoding: "raw",
78
- resize: "vertical",
79
- preview_styles: "font-family font-size font-weight font-style text-decoration text-transform",
80
- end_container_on_empty_block: true,
81
- wpeditimage_html5_captions: true,
82
- forced_root_block: "",
83
- force_br_newlines: false,
84
- force_p_newlines: true,
85
- selector: selector,
86
- plugins: wpforo.editor_settings.plugins,
87
- external_plugins: wpforo.editor_settings.external_plugins,
88
- menubar: false,
89
- toolbar: wpforo.editor_settings.tinymce.toolbar1,
90
- content_style: wpforo.editor_settings.tinymce.content_style,
91
- branding: false,
92
- elementpath: true,
93
- autoresize_on_init: wpforo.editor_settings.tinymce.autoresize_on_init,
94
- wp_autoresize_on: wpforo.editor_settings.tinymce.wp_autoresize_on,
95
- object_resizing: wpforo.editor_settings.tinymce.object_resizing,
96
- min_height: wpforo.editor_settings.editor_height,
97
- height: wpforo.editor_settings.editor_height,
98
- statusbar: true,
99
- wp_keep_scroll_position: wpforo.editor_settings.wp_keep_scroll_position,
100
- indent: wpforo.editor_settings.indent,
101
- add_unload_trigger: wpforo.editor_settings.add_unload_trigger,
102
- wpautop: wpforo.editor_settings.wpautop,
103
- fix_list_elements: true,
104
- browser_spellcheck: true,
105
- setup: wpforo.editor_settings.tinymce.setup,
106
- content_css: wpforo.editor_settings.tinymce.content_css,
107
- extended_valid_elements: wpforo.editor_settings.tinymce.extended_valid_elements,
108
- custom_elements: wpforo.editor_settings.tinymce.custom_elements
109
- }).then(function (e) {
110
- if (!do_not_focus && e.length) {
111
- wpforo_editor.focus(e[0].id);
112
- wpforo_editor.set_active(e[0].id);
113
  }
114
  });
115
  }
@@ -368,7 +367,7 @@ function wpforo_dialog_show(title, content, w, h){
368
  var dialog_body = $wpf("#wpforo-dialog-body", dialog);
369
  dialog_body.children().appendTo('#wpforo-dialog-backups');
370
  dialog_body.empty();
371
- if( content instanceof $wpf || content instanceof Element){
372
  content = $wpf(content);
373
  content.appendTo(dialog_body);
374
  content.show();
@@ -450,7 +449,7 @@ function wpforo_getTextSelection(){
450
  }
451
 
452
  window.wpforo_fix_form_data_attributes = function(){
453
- $wpf('form textarea[id^="wpf_"]:first').each(function(){
454
  var form = $wpf(this).closest('form');
455
  var id = $wpf(this).attr('id');
456
  form.attr('data-textareaid', id);
@@ -463,7 +462,7 @@ $wpf(document).ready(function($){
463
  var wpforo_wrap = $('#wpforo-wrap');
464
 
465
  var scroll_to;
466
- var exreg = new RegExp('\/' + wpforo.template_slugs['postid'] + '\/(\\d+)\/?$', 'i');
467
  var match = location.pathname.match(exreg);
468
  if(match){
469
  scroll_to = $('#post-' + match[1]);
@@ -498,6 +497,7 @@ $wpf(document).ready(function($){
498
  };
499
 
500
  window.wpforo_fix_form_data_attributes();
 
501
 
502
  setTimeout(function () {
503
  wpforo_editor.fix_textareaid();
@@ -612,7 +612,7 @@ $wpf(document).ready(function($){
612
  var stat = form.is( ":hidden" );
613
  form.slideToggle( "slow" );
614
  wpforo_editor.set_content('');
615
- $('[name="topic[title]"]').trigger("focus");
616
  var add_wpftopic = '<i class="fas fa-times" aria-hidden="true"></i>';
617
  if( !stat ) add_wpftopic = $('input[type="submit"]', form).val();
618
  $(this).html(add_wpftopic);
@@ -623,22 +623,20 @@ $wpf(document).ready(function($){
623
  $(this).closest('.wpf-bottom-bar').hide();
624
  });
625
 
626
- wpforo_wrap.on('click', '.wpfl-4 .add_wpftopic:not(.not_reg_user)', function(){
627
- var wrap = $(this).parents('div.wpfl-4');
628
  var form_wrap = $( ".wpf-topic-form-extra-wrap", wrap );
629
 
630
  var is_just_one_forum = form_wrap.data('is_just_one_forum');
631
  if( !is_just_one_forum ) $( '.wpf-topic-form-ajax-wrap').empty();
632
  var stat = form_wrap.is( ":hidden" );
633
- var btn = $(".wpfl-4 .add_wpftopic");
634
- btn.html(btn.data('phrase'));
635
- $(".wpf-topic-form-extra-wrap").slideUp("slow");
636
  var add_wpftopic;
637
  if( stat ){
638
  add_wpftopic = '<i class="fas fa-times" aria-hidden="true"></i>';
639
  form_wrap.slideDown("slow");
640
  }else{
641
- add_wpftopic = $(this).data('phrase');
642
  form_wrap.slideUp("slow");
643
  }
644
  if( !is_just_one_forum ){
@@ -646,7 +644,7 @@ $wpf(document).ready(function($){
646
  option_no_selected.show();
647
  option_no_selected.prop('selected', true);
648
  }
649
- $( this ).html(add_wpftopic);
650
  $('html, body').animate({ scrollTop: (wrap.offset().top -30 ) }, 415);
651
  });
652
 
@@ -663,9 +661,9 @@ $wpf(document).ready(function($){
663
  /* Home page loyouts toipcs toglle */
664
  wpforo_wrap.on('click', ".topictoggle", function(){
665
  wpforo_load_show();
666
-
667
  var id = $(this).attr( 'id' );
668
-
669
  id = id.replace( "img-arrow-", "" );
670
  $( ".wpforo-last-topics-" + id ).slideToggle( "slow" );
671
  if( $(this).hasClass('topictoggle') && $(this).hasClass('fa-chevron-down') ){
@@ -673,7 +671,7 @@ $wpf(document).ready(function($){
673
  }else{
674
  $( '#img-arrow-' + id ).removeClass('fa-chevron-up').addClass('fa-chevron-down');
675
  }
676
-
677
  id = id.replace( "button-arrow-", "" );
678
  $( ".wpforo-last-posts-" + id ).slideToggle( "slow" );
679
  if( $(this).hasClass('topictoggle') && $(this).hasClass('wpfcl-a') && $(this).hasClass('fa-chevron-down') ){
@@ -684,7 +682,7 @@ $wpf(document).ready(function($){
684
 
685
  wpforo_load_hide();
686
  });
687
-
688
  /* Home page loyouts toipcs toglle */
689
  wpforo_wrap.on('click', ".wpforo-membertoggle", function(){
690
  var id = $(this).attr( 'id' );
@@ -710,8 +708,8 @@ $wpf(document).ready(function($){
710
  $(this).find( ".wpforo-ttgg" ).attr('wpf-tooltip', wpforo_phrase('Show Replies'));
711
  }
712
  });
713
-
714
-
715
  //Reply
716
  wpforo_wrap.on('click', ".wpforo-reply:not(.wpforo_layout_4)", function(){
717
  wpforo_load_show();
@@ -737,9 +735,9 @@ $wpf(document).ready(function($){
737
  $('html, body').animate({ scrollTop: wrap.offset().top }, 500);
738
 
739
  wpforo_load_hide();
740
-
741
  });
742
-
743
  //Answer
744
  wpforo_wrap.on('click', ".wpforo-answer", function(){
745
  wpforo_load_show();
@@ -765,9 +763,9 @@ $wpf(document).ready(function($){
765
  $('html, body').animate({ scrollTop: wrap.offset().top }, 500);
766
 
767
  wpforo_load_hide();
768
-
769
  });
770
-
771
  wpforo_wrap.on('click', '.wpforo-qa-comment, .wpforo-reply.wpf-action.wpforo_layout_4', function () {
772
  var wrap = $(this).parents('.reply-wrap,.wpforo-qa-item-wrap');
773
  var post_wrap = $('.post-wrap', wrap);
@@ -809,7 +807,7 @@ $wpf(document).ready(function($){
809
  $('.wpforo-qa-comment,.wpforo-reply.wpf-action.wpforo_layout_4').show();
810
  wpforo_editor.set_content('');
811
  });
812
-
813
  //mobile menu responsive toggle
814
  wpforo_wrap.on('click', "#wpforo-menu .wpf-res-menu", function(){
815
  $("#wpforo-menu .wpf-menu").toggle();
@@ -828,7 +826,7 @@ $wpf(document).ready(function($){
828
  $("#wpforo-menu .wpf-search-field").css('transition-duration', '0.4s');
829
  });
830
  }
831
-
832
  // password show/hide switcher */
833
  wpforo_wrap.on('click', '.wpf-show-password', function () {
834
  var btn = $(this);
@@ -844,7 +842,7 @@ $wpf(document).ready(function($){
844
  btn.addClass('fa-eye-slash');
845
  }
846
  });
847
-
848
  //Turn off on dev mode
849
  //$(window).on('resize', function(){ if (window.RT) { clearTimeout(window.RT); } window.RT = setTimeout(function(){ this.location.reload(false);}, 100); });
850
 
@@ -1021,3 +1019,31 @@ $wpf(document).ready(function($){
1021
  if( val.match(exreg) ) location.href = val;
1022
  });
1023
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  function wpforo_tinymce_initializeIt(selector, do_not_focus) {
70
  if( wpforo_editor.is_tinymce_loaded() ){
71
+ $wpf( selector ).each(function(){
72
+ if( this.id ){
73
+ var editor_settings = window['wpforo_editor_settings_' + this.id] || wpforo['editor_settings'];
74
+ var settings = {
75
+ selector: '#' + this.id,
76
+ relative_urls: false,
77
+ remove_script_host: false,
78
+ convert_urls: false,
79
+ keep_styles: false,
80
+ end_container_on_empty_block: true,
81
+ wpeditimage_html5_captions: true,
82
+ force_br_newlines: false,
83
+ force_p_newlines: true,
84
+ menubar: false,
85
+ branding: false,
86
+ elementpath: true,
87
+ statusbar: true,
88
+ fix_list_elements: true,
89
+ browser_spellcheck: true,
90
+ entities: '38,amp,60,lt,62,gt',
91
+ entity_encoding: 'raw',
92
+ resize: 'vertical',
93
+ preview_styles: 'font-family font-size font-weight font-style text-decoration text-transform',
94
+ forced_root_block: '',
95
+ plugins: editor_settings['plugins'],
96
+ external_plugins: editor_settings['external_plugins'],
97
+ min_height: editor_settings['editor_height'],
98
+ indent: editor_settings['indent'],
99
+ height: editor_settings['editor_height'],
100
+ wpautop: editor_settings['wpautop'],
101
+ wp_keep_scroll_position: editor_settings['wp_keep_scroll_position'],
102
+ add_unload_trigger: editor_settings['add_unload_trigger']
103
+ };
104
+ settings = Object.assign(settings, editor_settings['tinymce']);
105
+ tinymce.init(settings).then(function (e) {
106
+ if (!do_not_focus && e.length) {
107
+ wpforo_editor.focus(e[0].id);
108
+ wpforo_editor.set_active(e[0].id);
109
+ }
110
+ });
111
+
 
112
  }
113
  });
114
  }
367
  var dialog_body = $wpf("#wpforo-dialog-body", dialog);
368
  dialog_body.children().appendTo('#wpforo-dialog-backups');
369
  dialog_body.empty();
370
+ if( content instanceof $wpf || content instanceof Element ){
371
  content = $wpf(content);
372
  content.appendTo(dialog_body);
373
  content.show();
449
  }
450
 
451
  window.wpforo_fix_form_data_attributes = function(){
452
+ $wpf('form textarea[data-isbody=1]:first').each(function(){
453
  var form = $wpf(this).closest('form');
454
  var id = $wpf(this).attr('id');
455
  form.attr('data-textareaid', id);
462
  var wpforo_wrap = $('#wpforo-wrap');
463
 
464
  var scroll_to;
465
+ var exreg = new RegExp('\/' + wpforo.settings_slugs['postid'] + '\/(\\d+)\/?$', 'i');
466
  var match = location.pathname.match(exreg);
467
  if(match){
468
  scroll_to = $('#post-' + match[1]);
497
  };
498
 
499
  window.wpforo_fix_form_data_attributes();
500
+ wpforo_tinymce_initializeIt('[data-wpforoeditor="tinymce"]', true);
501
 
502
  setTimeout(function () {
503
  wpforo_editor.fix_textareaid();
612
  var stat = form.is( ":hidden" );
613
  form.slideToggle( "slow" );
614
  wpforo_editor.set_content('');
615
+ $('[name="thread[title]"]').trigger("focus");
616
  var add_wpftopic = '<i class="fas fa-times" aria-hidden="true"></i>';
617
  if( !stat ) add_wpftopic = $('input[type="submit"]', form).val();
618
  $(this).html(add_wpftopic);
623
  $(this).closest('.wpf-bottom-bar').hide();
624
  });
625
 
626
+ wpforo_wrap.on('click', '.wpforo-section .add_wpftopic:not(.not_reg_user)', function(){
627
+ var wrap = $(this).parents('div.wpforo-section');
628
  var form_wrap = $( ".wpf-topic-form-extra-wrap", wrap );
629
 
630
  var is_just_one_forum = form_wrap.data('is_just_one_forum');
631
  if( !is_just_one_forum ) $( '.wpf-topic-form-ajax-wrap').empty();
632
  var stat = form_wrap.is( ":hidden" );
633
+ $( ".wpf-topic-form-extra-wrap" ).slideUp("slow");
 
 
634
  var add_wpftopic;
635
  if( stat ){
636
  add_wpftopic = '<i class="fas fa-times" aria-hidden="true"></i>';
637
  form_wrap.slideDown("slow");
638
  }else{
639
+ add_wpftopic = '<i class="fas fa-feather-alt" aria-hidden="true"></i>' + $(this).data('phrase');
640
  form_wrap.slideUp("slow");
641
  }
642
  if( !is_just_one_forum ){
644
  option_no_selected.show();
645
  option_no_selected.prop('selected', true);
646
  }
647
+ $( this ).html( add_wpftopic );
648
  $('html, body').animate({ scrollTop: (wrap.offset().top -30 ) }, 415);
649
  });
650
 
661
  /* Home page loyouts toipcs toglle */
662
  wpforo_wrap.on('click', ".topictoggle", function(){
663
  wpforo_load_show();
664
+
665
  var id = $(this).attr( 'id' );
666
+
667
  id = id.replace( "img-arrow-", "" );
668
  $( ".wpforo-last-topics-" + id ).slideToggle( "slow" );
669
  if( $(this).hasClass('topictoggle') && $(this).hasClass('fa-chevron-down') ){
671
  }else{
672
  $( '#img-arrow-' + id ).removeClass('fa-chevron-up').addClass('fa-chevron-down');
673
  }
674
+
675
  id = id.replace( "button-arrow-", "" );
676
  $( ".wpforo-last-posts-" + id ).slideToggle( "slow" );
677
  if( $(this).hasClass('topictoggle') && $(this).hasClass('wpfcl-a') && $(this).hasClass('fa-chevron-down') ){
682
 
683
  wpforo_load_hide();
684
  });
685
+
686
  /* Home page loyouts toipcs toglle */
687
  wpforo_wrap.on('click', ".wpforo-membertoggle", function(){
688
  var id = $(this).attr( 'id' );
708
  $(this).find( ".wpforo-ttgg" ).attr('wpf-tooltip', wpforo_phrase('Show Replies'));
709
  }
710
  });
711
+
712
+
713
  //Reply
714
  wpforo_wrap.on('click', ".wpforo-reply:not(.wpforo_layout_4)", function(){
715
  wpforo_load_show();
735
  $('html, body').animate({ scrollTop: wrap.offset().top }, 500);
736
 
737
  wpforo_load_hide();
738
+
739
  });
740
+
741
  //Answer
742
  wpforo_wrap.on('click', ".wpforo-answer", function(){
743
  wpforo_load_show();
763
  $('html, body').animate({ scrollTop: wrap.offset().top }, 500);
764
 
765
  wpforo_load_hide();
766
+
767
  });
768
+
769
  wpforo_wrap.on('click', '.wpforo-qa-comment, .wpforo-reply.wpf-action.wpforo_layout_4', function () {
770
  var wrap = $(this).parents('.reply-wrap,.wpforo-qa-item-wrap');
771
  var post_wrap = $('.post-wrap', wrap);
807
  $('.wpforo-qa-comment,.wpforo-reply.wpf-action.wpforo_layout_4').show();
808
  wpforo_editor.set_content('');
809
  });
810
+
811
  //mobile menu responsive toggle
812
  wpforo_wrap.on('click', "#wpforo-menu .wpf-res-menu", function(){
813
  $("#wpforo-menu .wpf-menu").toggle();
826
  $("#wpforo-menu .wpf-search-field").css('transition-duration', '0.4s');
827
  });
828
  }
829
+
830
  // password show/hide switcher */
831
  wpforo_wrap.on('click', '.wpf-show-password', function () {
832
  var btn = $(this);
842
  btn.addClass('fa-eye-slash');
843
  }
844
  });
845
+
846
  //Turn off on dev mode
847
  //$(window).on('resize', function(){ if (window.RT) { clearTimeout(window.RT); } window.RT = setTimeout(function(){ this.location.reload(false);}, 100); });
848
 
1019
  if( val.match(exreg) ) location.href = val;
1020
  });
1021
  });
1022
+
1023
+ /**
1024
+ *
1025
+ * @param { jQuery|Element|Document } element
1026
+ * @param { string } name
1027
+ * @param { string|number|integer } value
1028
+ */
1029
+ function wpforo_setAttr( element, name, value ){
1030
+ if( element instanceof $wpf || element instanceof Element ){
1031
+ element = $wpf( element );
1032
+ element.prop( name, value );
1033
+ element.attr( name, value );
1034
+
1035
+ var match = name.match( new RegExp('^data-(.+)', 'i') );
1036
+ if( match ) element.data( match[1], value );
1037
+ }
1038
+ }
1039
+
1040
+ function wpforo_confirm( msg, e ){
1041
+ if( !confirm( msg ) ){
1042
+ e = e || window.event;
1043
+ e.stopPropagation();
1044
+ e.preventDefault();
1045
+ return false;
1046
+ }
1047
+
1048
+ return true;
1049
+ }
{wpf-assets → assets/js}/index.html RENAMED
File without changes
{wpf-assets → assets}/js/snfb.js RENAMED
File without changes
{wpf-assets → assets}/js/tinymce-code.js RENAMED
File without changes
{wpf-assets → assets}/js/tinymce-emoji.js RENAMED
File without changes
{wpf-assets → assets}/js/tinymce-link.js RENAMED
File without changes
{wpf-assets → assets}/js/tinymce-pre.js RENAMED
File without changes
{wpf-assets → assets}/js/tinymce-spoiler.js RENAMED
File without changes
{wpf-assets → assets}/js/widgets.js RENAMED
File without changes
autoload.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use wpforo\wpforo;
4
+
5
+ spl_autoload_register( function( $namespace ) {
6
+ if( strpos( $namespace, 'wpforo' ) === 0 || strpos( $namespace, 'go2wpforo' ) === 0 ) {
7
+ $filepath = rtrim(
8
+ trim(
9
+ str_replace(
10
+ [ '/', '\\', '\\\\' ], DIRECTORY_SEPARATOR, WP_PLUGIN_DIR . "\\" . $namespace
11
+ )
12
+ ),
13
+ DIRECTORY_SEPARATOR
14
+ ) . ".php";
15
+ if( is_file( $filepath ) && is_readable( $filepath ) ) require_once $filepath;
16
+ }
17
+ } );
18
+
19
+ /**
20
+ * Main instance of wpForo.
21
+ *
22
+ * Returns the main instance of WPF to prevent the need to use globals.
23
+ *
24
+ * @return wpforo
25
+ * @since 1.4.3
26
+ */
27
+ if( ! function_exists( 'WPF' ) ) {
28
+ function WPF() {
29
+ return wpforo::instance();
30
+ }
31
+ }
32
+
33
+ // Global for backwards compatibility.
34
+ $GLOBALS['wpforo'] = WPF();
classes/API.php ADDED
@@ -0,0 +1,961 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ use WP_Error;
6
+
7
+ // Exit if accessed directly
8
+ if( ! defined( 'ABSPATH' ) ) exit;
9
+
10
+ class API {
11
+ const FB_SDK_VERSION = 'v2.10';
12
+ public $locale = 'en_US';
13
+ public $locale_iso = 'en';
14
+ public $fb_local = [
15
+ 'af_ZA',
16
+ 'ar_AR',
17
+ 'az_AZ',
18
+ 'be_BY',
19
+ 'bg_BG',
20
+ 'bn_IN',
21
+ 'bs_BA',
22
+ 'ca_ES',
23
+ 'cs_CZ',
24
+ 'cy_GB',
25
+ 'da_DK',
26
+ 'de_DE',
27
+ 'el_GR',
28
+ 'en_US',
29
+ 'en_GB',
30
+ 'eo_EO',
31
+ 'es_ES',
32
+ 'es_LA',
33
+ 'et_EE',
34
+ 'eu_ES',
35
+ 'fa_IR',
36
+ 'fb_LT',
37
+ 'fi_FI',
38
+ 'fo_FO',
39
+ 'fr_FR',
40
+ 'fr_CA',
41
+ 'fy_NL',
42
+ 'ga_IE',
43
+ 'gl_ES',
44
+ 'he_IL',
45
+ 'hi_IN',
46
+ 'hr_HR',
47
+ 'hu_HU',
48
+ 'hy_AM',
49
+ 'id_ID',
50
+ 'is_IS',
51
+ 'it_IT',
52
+ 'ja_JP',
53
+ 'ka_GE',
54
+ 'km_KH',
55
+ 'ko_KR',
56
+ 'ku_TR',
57
+ 'la_VA',
58
+ 'lt_LT',
59
+ 'lv_LV',
60
+ 'mk_MK',
61
+ 'ml_IN',
62
+ 'ms_MY',
63
+ 'nb_NO',
64
+ 'ne_NP',
65
+ 'nl_NL',
66
+ 'nn_NO',
67
+ 'pa_IN',
68
+ 'pl_PL',
69
+ 'ps_AF',
70
+ 'pt_PT',
71
+ 'pt_BR',
72
+ 'ro_RO',
73
+ 'ru_RU',
74
+ 'sk_SK',
75
+ 'sl_SI',
76
+ 'sq_AL',
77
+ 'sr_RS',
78
+ 'sv_SE',
79
+ 'sw_KE',
80
+ 'ta_IN',
81
+ 'te_IN',
82
+ 'th_TH',
83
+ 'tl_PH',
84
+ 'tr_TR',
85
+ 'uk_UA',
86
+ 'vi_VN',
87
+ 'zh_CN',
88
+ 'zh_HK',
89
+ 'zh_TW',
90
+ ];
91
+ public $tw_local = [
92
+ 'en',
93
+ 'ar',
94
+ 'bn',
95
+ 'cs',
96
+ 'da',
97
+ 'de',
98
+ 'el',
99
+ 'es',
100
+ 'fa',
101
+ 'fi',
102
+ 'fil',
103
+ 'fr',
104
+ 'he',
105
+ 'hi',
106
+ 'hu',
107
+ 'id',
108
+ 'it',
109
+ 'ja',
110
+ 'ko',
111
+ 'msa',
112
+ 'nl',
113
+ 'no',
114
+ 'pl',
115
+ 'pt',
116
+ 'ro',
117
+ 'ru',
118
+ 'sv',
119
+ 'th',
120
+ 'tr',
121
+ 'uk',
122
+ 'ur',
123
+ 'vi',
124
+ 'zh-cn',
125
+ 'zh-tw',
126
+ ];
127
+ public $ok_local = [ "ru", "en", "uk", "hy", "mo", "ro", "kk", "uz", "az", "tr" ];
128
+
129
+ public function __construct() {
130
+ add_action( 'wpforo_after_init', function() {
131
+ if( ! is_admin() ) {
132
+ $this->init_wp_recaptcha();
133
+ $this->hooks();
134
+ }
135
+ } );
136
+ }
137
+
138
+ private function hooks() {
139
+ $template = WPF()->current_object['template'];
140
+
141
+ ###############################################################################
142
+ ############### Facebook & Twitter API ########################################
143
+ ###############################################################################
144
+
145
+ if( ! is_user_logged_in() ) {
146
+ if( wpforo_setting( 'authorization', 'fb_login' ) ) {
147
+ if( in_array( $template, ['login','register'], true ) ) {
148
+ add_action( 'wp_enqueue_scripts', [ $this, 'fb_enqueue' ] );
149
+ add_action( 'wpforo_bottom_hook', [ $this, 'fb_login_sdk' ], 9 );
150
+ }
151
+ if( wpforo_setting( 'authorization', 'fb_api_id' ) && wpforo_setting( 'authorization', 'fb_api_secret' ) ) {
152
+ if( wpforo_setting( 'authorization', 'fb_lb_on_lp' ) ) {
153
+ add_action( 'wpforo_login_form_end', [ $this, 'fb_login_button' ] );
154
+ }
155
+ if( wpforo_setting( 'authorization', 'fb_lb_on_rp' ) ) {
156
+ add_action( 'wpforo_register_form_end', [ $this, 'fb_login_button' ] );
157
+ }
158
+ }
159
+ add_action( 'wp_ajax_wpforo_facebook_auth', [ $this, 'fb_auth' ] );
160
+ add_action( 'wp_ajax_nopriv_wpforo_facebook_auth', [ $this, 'fb_auth' ] );
161
+ }
162
+ }
163
+
164
+ if( is_wpforo_page() ) {
165
+ if( apply_filters( 'wpforo_api_fb_load_sdk', true ) ) {
166
+ add_action( 'wpforo_bottom_hook', [ $this, 'fb_sdk' ], 10 );
167
+ }
168
+ if( apply_filters( 'wpforo_api_tw_load_wjs', true ) && wpforo_setting( 'social', 'sb', 'tw' ) ) {
169
+ add_action( 'wpforo_top_hook', [ $this, 'tw_wjs' ], 11 );
170
+ }
171
+ if( apply_filters( 'wpforo_api_vk_load_js', true ) && wpforo_setting( 'social', 'sb', 'vk' ) ) {
172
+ add_action( 'wpforo_top_hook', [ $this, 'vk_js' ], 13 );
173
+ }
174
+ }
175
+
176
+ ###############################################################################
177
+ ############### reCAPTCHA API #################################################
178
+ ###############################################################################
179
+
180
+ $site_key = wpforo_setting( 'recaptcha', 'site_key' );
181
+ $secret_key = wpforo_setting( 'recaptcha', 'secret_key' );
182
+
183
+ if( ! is_user_logged_in() && $site_key && $secret_key ) {
184
+
185
+ $wpf_reg_form = wpforo_setting( 'recaptcha', 'wpf_reg_form' );
186
+ $wpf_login_form = wpforo_setting( 'recaptcha', 'wpf_login_form' );
187
+ $wpf_lostpass_form = wpforo_setting( 'recaptcha', 'wpf_lostpass_form' );
188
+
189
+ add_filter( 'script_loader_tag', [ &$this, 'rc_enqueue_async' ], 10, 3 );
190
+
191
+ //Verification Hooks: Login / Register / Reset Pass
192
+ if( $wpf_login_form ) add_filter( 'wp_authenticate_user', [ $this, 'rc_verify_wp_login' ], 15, 2 );
193
+ if( $wpf_reg_form ) add_filter( 'registration_errors', [ $this, 'rc_verify_wp_register' ], 10, 3 );
194
+ if( $wpf_lostpass_form ) add_action( 'lostpassword_post', [ $this, 'rc_verify_wp_lostpassword' ], 10 );
195
+
196
+ //Load reCAPTCHA API on wpForo pages: Login / Register / Reset Pass
197
+ if( in_array( $template, [ 'login', 'register', 'lostpassword' ], true ) ) {
198
+ if( $wpf_reg_form || $wpf_login_form || $wpf_lostpass_form ) {
199
+ add_action( 'wp_enqueue_scripts', [ $this, 'rc_enqueue' ] );
200
+ }
201
+ }
202
+
203
+ //Load reCAPTCHA Widget wpForo forms: Login / Register / Reset Pass
204
+ if( $wpf_login_form && $template === 'login' ) add_action( 'login_form', [ $this, 'rc_widget' ] );
205
+ if( $wpf_reg_form && $template === 'register' ) add_action( 'register_form', [ $this, 'rc_widget' ] );
206
+ if( $wpf_lostpass_form && $template === 'lostpassword' ) {
207
+ add_action( 'lostpassword_form', [ $this, 'rc_widget' ] );
208
+ }
209
+
210
+ //Load reCAPTCHA API and Widget for Topic and Post Editor
211
+ if( $template === 'forum' || $template === 'topic' || $template === 'post' ) {
212
+ add_action( 'wp_enqueue_scripts', [ $this, 'rc_enqueue' ] );
213
+ add_action( 'wpforo_verify_form_end', [ $this, 'rc_verify' ] );
214
+ if( wpforo_setting( 'recaptcha', 'topic_editor' ) ) {
215
+ add_action( 'wpforo_topic_form_extra_fields_after', [ $this, 'rc_widget' ] );
216
+ }
217
+ if( wpforo_setting( 'recaptcha', 'post_editor' ) ) {
218
+ add_action( 'wpforo_reply_form_extra_fields_after', [ $this, 'rc_widget' ] );
219
+ add_action( 'wpforo_portable_form_extra_fields_after', [ $this, 'rc_widget' ] );
220
+ }
221
+ }
222
+ }
223
+
224
+ ###############################################################################
225
+ }
226
+
227
+ private function init_wp_recaptcha() {
228
+ $template = WPF()->current_object['template'];
229
+ $site_key = wpforo_setting( 'recaptcha', 'site_key' );
230
+ $secret_key = wpforo_setting( 'recaptcha', 'secret_key' );
231
+
232
+ if( ! is_user_logged_in() && $site_key && $secret_key ) {
233
+ $reg_form = wpforo_setting( 'recaptcha', 'reg_form' );
234
+ $login_form = wpforo_setting( 'recaptcha', 'login_form' );
235
+ $lostpass_form = wpforo_setting( 'recaptcha', 'lostpass_form' );
236
+
237
+ //Verification Hooks: Login / Register / Reset Pass
238
+ if( $login_form ) add_filter( 'wp_authenticate_user', [ $this, 'rc_verify_wp_login' ], 15, 2 );
239
+ if( $reg_form ) add_filter( 'registration_errors', [ $this, 'rc_verify_wp_register' ], 10, 3 );
240
+ if( $lostpass_form ) add_action( 'lostpassword_post', [ $this, 'rc_verify_wp_lostpassword' ], 10 );
241
+
242
+ //Load reCAPTCHA API and Widget on wp-login.php
243
+ if( $reg_form || $login_form || $lostpass_form ) {
244
+ add_action( 'login_enqueue_scripts', [ $this, 'rc_enqueue' ] );
245
+ add_action( 'login_enqueue_scripts', [ $this, 'rc_enqueue_css' ] );
246
+ if( $login_form && $template !== 'login' ) add_action( 'login_form', [ $this, 'rc_widget' ] );
247
+ if( $reg_form && $template !== 'register' ) add_action( 'register_form', [ $this, 'rc_widget' ] );
248
+ if( $lostpass_form && $template !== 'lostpassword' ) {
249
+ add_action( 'lostpassword_form', [ $this, 'rc_widget' ] );
250
+ }
251
+ }
252
+ }
253
+ }
254
+
255
+ public function local( $api ) {
256
+ $wplocal = get_locale();
257
+ $wplocal_iso = substr( $wplocal, 0, 2 );
258
+
259
+ if( $api === 'fb' ) {
260
+ if( in_array( $wplocal, $this->fb_local ) ) {
261
+ return $wplocal;
262
+ } else {
263
+ return $this->locale;
264
+ }
265
+ } elseif( $api === 'tw' ) {
266
+ if( in_array( $wplocal_iso, $this->tw_local ) ) {
267
+ return $wplocal_iso;
268
+ } else {
269
+ return $this->locale_iso;
270
+ }
271
+ } elseif( $api === 'vk' ) {
272
+ return $wplocal_iso;
273
+ } elseif( $api === 'ok' ) {
274
+ if( in_array( $wplocal_iso, $this->ok_local ) ) {
275
+ return $wplocal_iso;
276
+ } else {
277
+ return $this->locale_iso;
278
+ }
279
+ }
280
+
281
+ return $wplocal;
282
+ }
283
+
284
+ public function fb_enqueue() {
285
+ $app_id = wpforo_setting( 'authorization', 'fb_api_id' );
286
+ wp_register_script( 'wpforo-snfb', WPFORO_URL . '/assets/js/snfb.js', [ 'jquery' ], WPFORO_VERSION, false );
287
+ wp_enqueue_script( 'wpforo-snfb' );
288
+ wp_localize_script( 'wpforo-snfb', 'wpforo_fb', [
289
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
290
+ 'site_url' => home_url(),
291
+ 'scopes' => 'email,public_profile',
292
+ 'appId' => $app_id,
293
+ 'l18n' => [
294
+ 'chrome_ios_alert' => __(
295
+ 'Please login into Facebook and then click connect button again',
296
+ 'wpforo'
297
+ ),
298
+ ],
299
+ ] );
300
+ }
301
+
302
+ public function fb_auth() {
303
+
304
+ $app_version = 'v2.10';
305
+ $app_secret = wpforo_setting( 'authorization', 'fb_api_secret' );
306
+ check_ajax_referer( 'wpforo-fb-nonce', 'security' );
307
+ $fb_token = isset( $_POST['fb_response']['authResponse']['accessToken'] ) ? $_POST['fb_response']['authResponse']['accessToken'] : '';
308
+ $fb_url = add_query_arg(
309
+ [ 'fields' => 'id,first_name,last_name,email,link,about,locale,birthday', 'access_token' => $fb_token ],
310
+ 'https://graph.facebook.com/' . $app_version . '/' . $_POST['fb_response']['authResponse']['userID']
311
+ );
312
+
313
+ ###################################################################################################################
314
+ // Verifying Graph API Calls with appsecret_proof
315
+ // Graph API calls can be made from clients or from your server on behalf of clients.
316
+ // Calls from a server can be better secured by adding a parameter called appsecret_proof.
317
+ // https://developers.facebook.com/docs/graph-api/securing-requests/
318
+ if( $app_secret ) {
319
+ $appsecret_proof = hash_hmac( 'sha256', $fb_token, trim( $app_secret ) );
320
+ $fb_url = add_query_arg( [ 'appsecret_proof' => $appsecret_proof ], $fb_url );
321
+ }
322
+ ###################################################################################################################
323
+
324
+ $fb_response = wp_remote_get( esc_url_raw( $fb_url ), [ 'timeout' => 30 ] );
325
+ if( is_wp_error( $fb_response ) ) wpforo_ajax_response( [ 'error' => $fb_response->get_error_message() ] );
326
+ $fb_user = json_decode( wp_remote_retrieve_body( $fb_response ), true );
327
+ if( isset( $fb_user['error'] ) ) {
328
+ wpforo_ajax_response( [ 'error' => 'Error code: ' . $fb_user['error']['code'] . ' - ' . $fb_user['error']['message'] ] );
329
+ }
330
+ if( empty( $fb_user['email'] ) ) {
331
+ wpforo_ajax_response( [
332
+ 'error' => __(
333
+ 'Your email is required to be able authorize you here. Please try loging again. ',
334
+ 'wpforo'
335
+ ),
336
+ 'fb' => $fb_user,
337
+ ] );
338
+ }
339
+ $fb_user['link'] = ( isset( $fb_user['link'] ) ) ? $fb_user['link'] : '';
340
+ $fb_user['about'] = ( isset( $fb_user['about'] ) ) ? $fb_user['about'] : '';
341
+ $fb_user['locale'] = ( isset( $fb_user['locale'] ) ) ? $fb_user['locale'] : '';
342
+ $user = [
343
+ 'fb_user_id' => $fb_user['id'],
344
+ 'first_name' => $fb_user['first_name'],
345
+ 'last_name' => $fb_user['last_name'],
346
+ 'user_email' => $fb_user['email'],
347
+ 'user_url' => $fb_user['link'],
348
+ 'user_pass' => wp_generate_password(),
349
+ 'description' => $fb_user['about'],
350
+ 'locale' => $fb_user['locale'],
351
+ 'rich_editing' => 'true',
352
+ ];
353
+ $message = [ 'error' => __( 'Invalid User', 'wpforo' ) ];
354
+ if( empty( $user['fb_user_id'] ) ) wpforo_ajax_response( $message );
355
+ $member = wpforo_get_fb_user( $user );
356
+ $meta_updated = false;
357
+
358
+ if( $member ) {
359
+ $user_id = $member->ID;
360
+ $message = [ 'success' => $user_id, 'method' => 'login' ];
361
+ if( empty( $member->user_email ) ) {
362
+ wp_update_user( [ 'ID' => $user_id, 'user_email' => $user['user_email'] ] );
363
+ }
364
+ } else {
365
+ if( ! wpforo_setting( 'authorization', 'user_register' ) ) {
366
+ wpforo_ajax_response( [ 'error' => __( 'User registration is disabled', 'wpforo' ) ] );
367
+ }
368
+ $username = wpforo_unique_username( $user['user_email'] );
369
+ $user['user_login'] = str_replace( '.', '', $username );
370
+ $user['user_nicename'] = sanitize_title( $username );
371
+ $user['display_name'] = ( $user['first_name'] || $user['last_name'] ) ? trim(
372
+ $user['first_name'] . ' ' . $user['last_name']
373
+ ) : ucfirst( str_replace( '-', ' ', $user['user_nicename'] ) );
374
+ $user_id = wp_insert_user( $user );
375
+ if( ! is_wp_error( $user_id ) ) {
376
+ wp_new_user_notification( $user_id, null, 'admin' );
377
+ wp_new_user_notification( $user_id, '', 'user' );
378
+ update_user_meta( $user_id, '_fb_user_id', $user['fb_user_id'] );
379
+ if( isset( $fb_user['birthday'] ) && $fb_user['birthday'] ) {
380
+ update_user_meta( $user_id, '_fb_user_birthday', $fb_user['birthday'] );
381
+ }
382
+ $meta_updated = true;
383
+ $message = [ 'success' => $user_id, 'method' => 'registration' ];
384
+ }
385
+ }
386
+ if( is_numeric( $user_id ) ) {
387
+ wp_set_auth_cookie( $user_id, true );
388
+ if( ! $meta_updated ) update_user_meta( $user_id, '_fb_user_id', $user['fb_user_id'] );
389
+ }
390
+ wpforo_ajax_response( $message );
391
+ }
392
+
393
+ public function fb_redirect() {
394
+ if( wpforo_setting( 'authorization', 'fb_redirect' ) === 'custom' && wpforo_setting( 'authorization', 'fb_redirect_url' ) != '' ) {
395
+ return esc_url( wpforo_setting( 'authorization', 'fb_redirect_url' ) );
396
+ } elseif( wpforo_setting( 'authorization', 'fb_redirect' ) === 'profile' ) {
397
+ return wpforo_home_url( "account/" );
398
+ } else {
399
+ return wpforo_home_url();
400
+ }
401
+ }
402
+
403
+ public function fb_sdk() {
404
+ ?>
405
+ <div id="fb-root"></div>
406
+ <script type='text/javascript'>(function (d, s, id) {
407
+ var js, fjs = d.getElementsByTagName(s)[0]
408
+ if (d.getElementById(id)) return
409
+ js = d.createElement(s)
410
+ js.id = id
411
+ js.src = "//connect.facebook.net/<?php echo $this->local(
412
+ 'fb'
413
+ ) ?>/sdk.js#xfbml=1&version=<?php echo self::FB_SDK_VERSION ?>&appId=<?php echo wpforo_setting( 'authorization', 'fb_api_id' ) ?>&autoLogAppEvents=1"
414
+ fjs.parentNode.insertBefore(js, fjs)
415
+ }(document, 'script', 'facebook-jssdk'))</script>
416
+ <?php
417
+ }
418
+
419
+ public function fb_login_sdk() {
420
+ ?>
421
+ <script type='text/javascript'>function statusChangeCallback (response) { if (response.status === 'connected') {} else if (response.status === 'not_authorized') {} else {}}
422
+
423
+ function checkLoginState () { FB.getLoginStatus(function (response) { statusChangeCallback(response) })}
424
+
425
+ window.fbAsyncInit = function () {
426
+ FB.init({
427
+ appId: '<?php echo trim(
428
+ wpforo_setting( 'authorization', 'fb_api_id' )
429
+ ) ?>',
430
+ cookie: <?php echo wpforo_setting( 'legal', 'cookies' ) ? 'true' : 'false'; ?>,
431
+ xfbml: true,
432
+ status: false,
433
+ version: '<?php echo self::FB_SDK_VERSION ?>'
434
+ })
435
+ FB.getLoginStatus(function (response) {statusChangeCallback(response)})
436
+ }</script>
437
+ <?php
438
+ }
439
+
440
+ public function fb_login_button() {
441
+ $checkbox = wpforo_setting( 'legal', 'checkbox_fb_login' );
442
+ $public_profile = '<a href="https://developers.facebook.com/docs/facebook-login/permissions#reference-public_profile" target="_blank" rel="nofollow" title="' . wpforo_phrase(
443
+ 'Read more about Facebook public_profile properties.',
444
+ false
445
+ ) . '">public_profile</a>';
446
+ ?>
447
+ <?php if( $checkbox ): ?>
448
+ <div class="wpforo-fb-info">
449
+ <span class="wpforo-fb-info-title">
450
+ <i class="fas fa-info-circle wpfcl-5" aria-hidden="true"
451
+ style="font-size:16px;"></i> &nbsp;<?php wpforo_phrase( 'Facebook Login Information' ); ?>
452
+ </span>
453
+ <span class="wpforo-fb-info-text">
454
+ <?php echo apply_filters(
455
+ 'wpforo_fb_login_privacy_info',
456
+ sprintf(
457
+ wpforo_phrase(
458
+ 'When you login first time using Facebook Login button, we collect your account %s information shared by Facebook, based on your privacy settings. We also get your email address to automatically create a forum account for you. Once your account is created, you\'ll be logged-in to this account and you\'ll receive a confirmation email.',
459
+ false
460
+ ),
461
+ $public_profile
462
+ )
463
+ ); ?>
464
+ </span>
465
+ <label class="wpforo-legal-checkbox wpflegal-fblogin">
466
+ <input id="wpflegal_fblogin" name="legal[agree-fb-login]" value="1" type="checkbox"> &nbsp;
467
+ <span><?php wpforo_phrase( 'I allow to create an account and send confirmation email.' ); ?></span>
468
+ </label>
469
+ </div>
470
+ <?php endif; ?>
471
+ <div class="wpforo_fb-button wpforo-fb-login-wrap" data-redirect="<?php echo $this->fb_redirect() ?>"
472
+ data-fb_nonce="<?php echo wp_create_nonce(
473
+ 'wpforo-fb-nonce'
474
+ ) ?>" <?php if( $checkbox ) echo 'style="pointer-events: none; opacity:0.6;"'; ?>>
475
+ <div class="fb-login-button" data-max-rows="1" onlogin="wpforo_fb_check_auth" data-size="medium"
476
+ data-button-type="login_with" data-show-faces="false" data-auth-type="rerequest"
477
+ data-auto-logout-link="false" data-use-continue-as="true" data-scope="email,public_profile"></div>
478
+ <img data-no-lazy="1" src="<?php echo WPFORO_URL . '/assets/images/loading.gif'; ?>"
479
+ class="wpforo_fb-spinner" style="display:none" alt="loading...">
480
+ </div>
481
+ <?php
482
+ }
483
+
484
+ public function fb_share_button( $url = '', $type = 'custom' ) {
485
+ if( ! wpforo_setting( 'social', 'sb', 'fb' ) || ! wpforo_setting( 'authorization', 'fb_api_id' ) ) return;
486
+ $url = $url ?: WPF()->current_url;
487
+ if( $type === 'custom' ) { ?>
488
+ <span class="wpforo-share-button wpf-fb" data-wpfurl="<?php echo $url ?>"
489
+ title="<?php wpforo_phrase( 'Share to Facebook' ); ?>">
490
+ <?php if( wpforo_setting( 'social', 'sb_icon' ) === 'figure' ): ?>
491
+ <i class="fab fa-facebook-f" aria-hidden="true"></i>
492
+ <?php elseif( wpforo_setting( 'social', 'sb_icon' ) === 'square' ): ?>
493
+ <i class="fab fa-facebook-square" aria-hidden="true"></i>
494
+ <?php else: ?>
495
+ <i class="fab fa-facebook" aria-hidden="true"></i>
496
+ <?php endif; ?>
497
+ </span>
498
+ <?php
499
+ } else {
500
+ ?>
501
+ <div class="wpf-sbw wpf-sbw-fb">
502
+ <?php if( wpforo_setting( 'social', 'sb_type' ) === 'button_count' ): ?>
503
+ <div class="fb-share-button" data-href="<?php echo esc_url( $url ) ?>" data-layout="button_count"
504
+ data-size="small" data-mobile-iframe="true">
505
+ <a target="_blank"
506
+ href="https://www.facebook.com/sharer/sharer.php?u=<?php echo urlencode( $url ) ?>"
507
+ class="fb-xfbml-parse-ignore"><?php wpforo_phrase( 'Share' ); ?></a>
508
+ </div>
509
+ <?php elseif( wpforo_setting( 'social', 'sb_type' ) === 'button' ): ?>
510
+ <span class="wpf-sb-button wpf-fb" data-wpfurl="<?php echo esc_url( $url ) ?>">
511
+ <i class="fab fa-facebook-f" aria-hidden="true"></i> <span><?php echo wpforo_phrase(
512
+ 'Share'
513
+ ) ?></span>
514
+ </span>
515
+ <?php else: ?>
516
+ <span class="wpf-sb-button wpf-sb-icon wpf-fb" data-wpfurl="<?php echo esc_url( $url ) ?>">
517
+ <i class="fab fa-facebook-f" aria-hidden="true"></i>
518
+ </span>
519
+ <?php endif; ?>
520
+ </div>
521
+ <?php
522
+ }
523
+ }
524
+
525
+ public function tw_wjs() {
526
+ ?>
527
+ <script type="text/javascript">window.twttr = (function (d, s, id) {
528
+ var js, fjs = d.getElementsByTagName(s)[0], t = window.twttr || {}
529
+ if (d.getElementById(id)) return t
530
+ js = d.createElement(s)
531
+ js.id = id
532
+ js.src = 'https://platform.twitter.com/widgets.js'
533
+ fjs.parentNode.insertBefore(js, fjs)
534
+ t._e = []
535
+ t.ready = function (f) { t._e.push(f) }
536
+ return t
537
+ }(document, 'script', 'twitter-wjs'))</script>
538
+ <?php
539
+ }
540
+
541
+ public function tw_share_button( $url = '', $type = 'custom', $text = '' ) {
542
+ if( ! wpforo_setting( 'social', 'sb', 'tw' ) ) return;
543
+ $url = $url ?: WPF()->current_url;
544
+ $n_url = strlen( $url );
545
+ $n_text = 280 - $n_url;
546
+ $text = $text ?: wpfval( WPF()->current_object, 'og_text' );
547
+ $text = urlencode( wpforo_text( strip_shortcodes( strip_tags( $text ) ), $n_text, false ) );
548
+ if( $type == 'custom' ) { ?>
549
+ <a class="wpforo-share-button wpf-tw"
550
+ href="https://twitter.com/intent/tweet?text=<?php echo $text ?>&url=<?php echo urlencode( $url ) ?>"
551
+ title="<?php wpforo_phrase( 'Tweet this post' ); ?>">
552
+ <?php if( wpforo_setting( 'social', 'sb_icon' ) === 'figure' ): ?>
553
+ <i class="fab fa-twitter" aria-hidden="true"></i>
554
+ <?php elseif( wpforo_setting( 'social', 'sb_icon' ) === 'square' ): ?>
555
+ <i class="fab fa-twitter-square" aria-hidden="true"></i>
556
+ <?php else: ?>
557
+ <i class="fab fa-twitter" aria-hidden="true"></i>
558
+ <?php endif; ?>
559
+ </a>
560
+ <?php
561
+ } else { ?>
562
+ <div class="wpf-sbw wpf-sbw-tw">
563
+ <?php if( wpforo_setting( 'social', 'sb_type' ) === 'button_count' ): ?>
564
+ <a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button"
565
+ data-lang="<?php $this->local( 'tw' ) ?>" data-show-count="true"><?php wpforo_phrase(
566
+ 'Tweet'
567
+ ); ?></a>
568
+ <?php elseif( wpforo_setting( 'social', 'sb_type' ) === 'button' ): ?>
569
+ <a class="wpf-sb-button wpf-tw"
570
+ href="https://twitter.com/intent/tweet?text=<?php echo $text ?>&url=<?php echo urlencode(
571
+ $url
572
+ ) ?>">
573
+ <i class="fab fa-twitter" aria-hidden="true"></i> <span><?php echo wpforo_phrase(
574
+ 'Tweet'
575
+ ) ?></span>
576
+ </a>
577
+ <?php else: ?>
578
+ <a class="wpf-sb-button wpf-sb-icon wpf-tw"
579
+ href="https://twitter.com/intent/tweet?text=<?php echo $text ?>&url=<?php echo urlencode(
580
+ $url
581
+ ) ?>">
582
+ <i class="fab fa-twitter" aria-hidden="true"></i>
583
+ </a>
584
+ <?php endif; ?>
585
+ </div>
586
+ <?php
587
+ }
588
+ }
589
+
590
+ public function vk_js() {
591
+ ?>
592
+ <script type="text/javascript" src="https://vk.com/js/api/share.js?95" charset="windows-1251"></script>
593
+ <?php
594
+ }
595
+
596
+ public function wapp_share_button( $url = '', $type = 'custom', $text = '' ) {
597
+ if( ! wpforo_setting( 'social', 'sb', 'wapp' ) ) return;
598
+ $url = $url ?: WPF()->current_url;
599
+ $domain = wp_is_mobile() ? 'https://api.whatsapp.com' : 'https://web.whatsapp.com';
600
+ $text = $text ?: ( wpfval( WPF()->current_object, 'og_text' ) ? WPF()->current_object['og_text'] : WPF()->board->get_current( 'settings' )['title'] );
601
+ $text = urlencode( wpforo_text( strip_shortcodes( strip_tags( $text ) ), 100, false ) ) . ' URL: ' . urlencode( $url );
602
+ if( $type === 'custom' ) { ?>
603
+ <a class="wpforo-share-button wpf-wapp" href="<?php echo $domain ?>/send?text=<?php echo $text ?>"
604
+ title="<?php wpforo_phrase( 'Share to WhatsApp' ); ?>" target="_blank"
605
+ data-action="share/whatsapp/share">
606
+ <i class="fab fa-whatsapp" aria-hidden="true"></i>
607
+ </a>
608
+ <?php
609
+ } else { ?>
610
+ <div class="wpf-sbw wpf-sbw-wapp">
611
+ <?php if( wpforo_setting( 'social', 'sb_type' ) === 'button_count' ): ?>
612
+ <!-- WhatsApp is not available -->
613
+ <?php elseif( wpforo_setting( 'social', 'sb_type' ) === 'button' ): ?>
614
+ <a class="wpf-sb-button wpf-wapp" href="<?php echo $domain ?>/send?text=<?php echo $text ?>"
615
+ target="_blank" data-action="share/whatsapp/share">
616
+ <i class="fab fa-whatsapp" aria-hidden="true"></i> <span><?php echo wpforo_phrase( 'Share' ) ?></span>
617
+ </a>
618
+ <?php else: ?>
619
+ <a class="wpf-sb-button wpf-sb-icon wpf-wapp"
620
+ href="<?php echo $domain ?>/send?text=<?php echo $text ?>" target="_blank"
621
+ data-action="share/whatsapp/share">
622
+ <i class="fab fa-whatsapp" aria-hidden="true"></i>
623
+ </a>
624
+ <?php endif; ?>
625
+ </div>
626
+ <?php
627
+ }
628
+ }
629
+
630
+ public function lin_share_button( $url = '', $type = 'custom', $text = '' ) {
631
+ if( ! wpforo_setting( 'social', 'sb', 'lin' ) ) return;
632
+ $url = $url ?: WPF()->current_url;
633
+ $title = wpfval( WPF()->current_object, 'topic', 'title' );
634
+ $text = $text ?: ( wpfval( WPF()->current_object, 'og_text' ) ? WPF()->current_object['og_text'] : WPF()->board->get_current(
635
+ 'settings'
636
+ )['title'] );
637
+ $text = urlencode( wpforo_text( strip_shortcodes( strip_tags( $text ) ), 500, false ) );
638
+ if( $type == 'custom' ) { ?>
639
+ <a class="wpforo-share-button wpf-lin"
640
+ href="https://www.linkedin.com/shareArticle?mini=true&url=<?php echo urlencode(
641
+ $url
642
+ ) ?>&title=<?php echo urlencode( $title ) ?>&summary=<?php echo $text ?>"
643
+ title="<?php wpforo_phrase( 'Share to LinkedIn' ); ?>" target="_blank">
644
+ <i class="fab fa-linkedin-in" aria-hidden="true"></i>
645
+ </a>
646
+ <?php
647
+ } else { ?>
648
+ <div class="wpf-sbw wpf-sbw-lin">
649
+ <?php if( wpforo_setting( 'social', 'sb_type' ) === 'button_count' ): ?>
650
+ <!-- LinkedIn is not available -->
651
+ <?php elseif( wpforo_setting( 'social', 'sb_type' ) === 'button' ): ?>
652
+ <a class="wpf-sb-button wpf-lin"
653
+ href="https://www.linkedin.com/shareArticle?mini=true&url=<?php echo urlencode(
654
+ $url
655
+ ) ?>&title=<?php echo urlencode( $title ) ?>&summary=<?php echo $text ?>" target="_blank">
656
+ <i class="fab fa-linkedin-in" aria-hidden="true"></i> <span><?php echo wpforo_phrase(
657
+ 'Share'
658
+ ) ?></span>
659
+ </a>
660
+ <?php else: ?>
661
+ <a class="wpf-sb-button wpf-sb-icon wpf-lin"
662
+ href="https://www.linkedin.com/shareArticle?mini=true&url=<?php echo urlencode(
663
+ $url
664
+ ) ?>&title=<?php echo urlencode( $title ) ?>&summary=<?php echo $text ?>" target="_blank">
665
+ <i class="fab fa-linkedin-in" aria-hidden="true"></i>
666
+ </a>
667
+ <?php endif; ?>
668
+ </div>
669
+ <?php
670
+ }
671
+ }
672
+
673
+ public function vk_share_button( $url = '', $type = 'custom', $text = '' ) {
674
+ if( ! wpforo_setting( 'social', 'sb', 'vk' ) ) return;
675
+ $url = $url ?: WPF()->current_url;
676
+ $text = $text ?: wpfval( WPF()->current_object, 'og_text' );
677
+ $text = urlencode( wpforo_text( strip_shortcodes( strip_tags( $text ) ), 500, false ) );
678
+ if( $type === 'custom' ) { ?>
679
+ <a class="wpforo-share-button wpf-vk" onclick="return VK.Share.click(0, this);"
680
+ href="https://vk.com/share.php?url=<?php echo urlencode( $url ) ?>&description=<?php echo $text ?>"
681
+ title="<?php wpforo_phrase( 'Share to VK' ); ?>" target="_blank">
682
+ <i class="fab fa-vk" aria-hidden="true"></i>
683
+ </a>
684
+ <?php
685
+ } else { ?>
686
+ <div class="wpf-sbw wpf-sbw-vk">
687
+ <?php if( wpforo_setting( 'social', 'sb_type' ) === 'button_count' ): ?>
688
+ <script type="text/javascript">document.write(VK.Share.button(false, {
689
+ type: 'round',
690
+ text: "<?php wpforo_phrase( 'Share' ); ?>"
691
+ }))</script>
692
+ <?php elseif( wpforo_setting( 'social', 'sb_type' ) === 'button' ): ?>
693
+ <a class="wpf-sb-button wpf-vk" onclick="return VK.Share.click(0, this);"
694
+ href="https://vk.com/share.php?url=<?php echo urlencode(
695
+ $url
696
+ ) ?>&description=<?php echo $text ?>" target="_blank">
697
+ <i class="fab fa-vk" aria-hidden="true"></i> <span><?php echo wpforo_phrase( 'Share' ) ?></span>
698
+ </a>
699
+ <?php else: ?>
700
+ <a class="wpf-sb-button wpf-sb-icon wpf-vk" onclick="return VK.Share.click(0, this);"
701
+ href="https://vk.com/share.php?url=<?php echo urlencode(
702
+ $url
703
+ ) ?>&description=<?php echo $text ?>" target="_blank">
704
+ <i class="fab fa-vk" aria-hidden="true"></i>
705
+ </a>
706
+ <?php endif; ?>
707
+ </div>
708
+ <?php
709
+ }
710
+ }
711
+
712
+ public function ok_share_button( $url = '', $type = 'custom', $text = '' ) {
713
+ if( ! wpforo_setting( 'social', 'sb', 'ok' ) ) return;
714
+ $url = $url ?: WPF()->current_url;
715
+ if( preg_match( '|\#post-(\d+)|s', $url, $a ) ) {
716
+ $pid = ( isset( $a[1] ) ) ? intval( $a[1] ) : mt_rand( 100000, 999999 );
717
+ } else {
718
+ $pid = mt_rand( 100000, 999999 );
719
+ }
720
+ $text = $text ?: wpfval( WPF()->current_object, 'og_text' );
721
+ $text = wpforo_text( strip_shortcodes( strip_tags( $text ) ), 500, false );
722
+ if( $type == 'custom' ) { ?>
723
+ <a class="wpforo-share-button wpf-ok"
724
+ href="https://connect.ok.ru/offer?url=<?php echo urlencode( $url ) ?>&description=<?php echo urlencode(
725
+ $text
726
+ ) ?>" title="<?php wpforo_phrase( 'Share to OK' ); ?>" target="_blank">
727
+ <?php if( wpforo_setting( 'social', 'sb_icon' ) === 'figure' ): ?>
728
+ <i class="fab fa-odnoklassniki" aria-hidden="true"></i>
729
+ <?php elseif( wpforo_setting( 'social', 'sb_icon' ) === 'square' ): ?>
730
+ <i class="fab fa-odnoklassniki-square" aria-hidden="true"></i>
731
+ <?php else: ?>
732
+ <i class="fab fa-odnoklassniki-square" aria-hidden="true"></i>
733
+ <?php endif; ?>
734
+ </a>
735
+ <?php
736
+ } else { ?>
737
+ <div class="wpf-sbw wpf-sbw-ok">
738
+ <?php if( wpforo_setting( 'social', 'sb_type' ) === 'button_count' ): ?>
739
+ <div id="<?php echo 'wpfokb_' . $pid ?>"></div>
740
+ <script>
741
+ !function (d, id, did, st, title, description, image) {
742
+ var js = d.createElement('script')
743
+ js.src = 'https://connect.ok.ru/connect.js'
744
+ js.onload = js.onreadystatechange = function () {
745
+ if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
746
+ if (!this.executed) {
747
+ this.executed = true
748
+ setTimeout(function () { OK.CONNECT.insertShareWidget(id, did, st, title, description, image) }, 0)
749
+ }
750
+ }
751
+ }
752
+ d.documentElement.appendChild(js)
753
+ }(document, "<?php echo 'wpfokb_' . $pid ?>", "<?php echo esc_attr(
754
+ $url
755
+ ) ?>", '{"sz":20,"st":"straight","ck":2,"lang":"<?php echo $this->local(
756
+ 'ok'
757
+ ) ?>"}', '', '', '')
758
+ </script>
759
+ <?php elseif( wpforo_setting( 'social', 'sb_type' ) === 'button' ): ?>
760
+ <a class="wpf-sb-button wpf-ok" href="https://connect.ok.ru/offer?url=<?php echo urlencode(
761
+ $url
762
+ ) ?>&description=<?php echo urlencode( $text ) ?>" title="<?php wpforo_phrase( 'Share to OK' ); ?>"
763
+ target="_blank">
764
+ <i class="fab fa-odnoklassniki" aria-hidden="true"></i> <span><?php echo wpforo_phrase(
765
+ 'Share'
766
+ ) ?></span>
767
+ </a>
768
+ <?php else: ?>
769
+ <a class="wpf-sb-button wpf-sb-icon wpf-ok"
770
+ href="https://connect.ok.ru/offer?url=<?php echo urlencode(
771
+ $url
772
+ ) ?>&description=<?php echo urlencode( $text ) ?>"
773
+ title="<?php wpforo_phrase( 'Share to OK' ); ?>" target="_blank">
774
+ <i class="fab fa-odnoklassniki" aria-hidden="true"></i>
775
+ </a>
776
+ <?php endif; ?>
777
+ </div>
778
+ <?php
779
+ }
780
+ }
781
+
782
+ public function share_toggle( $url = '', $text = '', $type = 'custom' ) {
783
+ WPF()->api->fb_share_button( $url, $type );
784
+ WPF()->api->tw_share_button( $url, $type, $text );
785
+ WPF()->api->wapp_share_button( $url, $type, $text );
786
+ WPF()->api->lin_share_button( $url, $type, $text );
787
+ WPF()->api->vk_share_button( $url, $type, $text );
788
+ WPF()->api->ok_share_button( $url, $type, $text );
789
+ }
790
+
791
+ public function share_buttons( $url = '', $type = 'default', $text = '' ) {
792
+ $template = wpfval( WPF()->current_object, 'template' );
793
+ $exclude = [ 'lostpassword', 'resetpassword' ];
794
+ if( $template && ! in_array( $template, $exclude ) ) {
795
+ WPF()->api->fb_share_button( $url, $type );
796
+ WPF()->api->tw_share_button( $url, $type, $text );
797
+ WPF()->api->wapp_share_button( $url, $type, $text );
798
+ WPF()->api->lin_share_button( $url, $type, $text );
799
+ WPF()->api->vk_share_button( $url, $type, $text );
800
+ WPF()->api->ok_share_button( $url, $type, $text );
801
+ }
802
+ }
803
+
804
+ public function rc_enqueue() {
805
+ $theme = wpforo_setting( 'recaptcha', 'theme' );
806
+ $site_key = wpforo_setting( 'recaptcha', 'site_key' );
807
+ wp_register_script(
808
+ 'wpforo_recaptcha',
809
+ 'https://www.google.com/recaptcha/api.js?onload=wpForoReCallback&render=explicit'
810
+ );
811
+ wp_add_inline_script(
812
+ 'wpforo_recaptcha',
813
+ "var wpForoReCallback = function(){
814
+ setTimeout(function () {
815
+ if( typeof grecaptcha !== 'undefined' && typeof grecaptcha.render === 'function' ){
816
+ var rc_widgets = document.getElementsByClassName('wpforo_recaptcha_widget');
817
+ if( rc_widgets.length ){
818
+ var i;
819
+ for (i = 0; i < rc_widgets.length; i++) {
820
+ if( rc_widgets[i].firstElementChild === null ){
821
+ rc_widgets[i].innerHtml = '';
822
+ grecaptcha.render(
823
+ rc_widgets[i], { 'sitekey': '" . $site_key . "', 'theme': '" . $theme . "' }
824
+ );
825
+ }
826
+ }
827
+ }
828
+ }
829
+ }, 800);
830
+ }"
831
+ );
832
+ wp_enqueue_script( 'wpforo_recaptcha' );
833
+ }
834
+
835
+ public function rc_enqueue_async( $tag, $handle ) {
836
+ if( $handle === 'wpforo_recaptcha' ) return str_replace( '<script', '<script async defer', $tag );
837
+
838
+ return $tag;
839
+ }
840
+
841
+ public function rc_enqueue_css() {
842
+ wp_register_style( 'wpforo-rc-style', false );
843
+ wp_enqueue_style( 'wpforo-rc-style' );
844
+ $custom_css = ".wpforo_recaptcha_widget{ -webkit-transform:scale(0.9); transform:scale(0.9); -webkit-transform-origin:left 0; transform-origin:left 0; }";
845
+ wp_add_inline_style( 'wpforo-rc-style', $custom_css );
846
+ }
847
+
848
+ public function rc_widget() {
849
+ $site_key = wpforo_setting( 'recaptcha', 'site_key' );
850
+ if( $site_key ) {
851
+ echo '<div class="wpforo_recaptcha_widget"></div><div class="wpf-cl"></div>';
852
+ echo "\r\n<script>wpForoReCallback();</script>";
853
+ }
854
+ }
855
+
856
+ public function rc_check() {
857
+ if( isset( $_POST['g-recaptcha-response'] ) ) {
858
+ $secret_key = wpforo_setting( 'recaptcha', 'secret_key' );
859
+ $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secret_key . '&response=' . $_POST['g-recaptcha-response'];
860
+ $response = wp_remote_get( $url );
861
+ if( is_wp_error( $response ) || empty( $response['body'] ) ) {
862
+ $error = wpforo_phrase( "ERROR: Can't connect to Google reCAPTCHA API", false );
863
+ if( WP_DEBUG === true ) $error .= ' ( ' . $response->get_error_message() . ' )';
864
+
865
+ return $error;
866
+ }
867
+ $response = json_decode( $response['body'], true );
868
+ if( $response['success'] == true ) {
869
+ return 'success';
870
+ } else {
871
+ return wpforo_phrase( 'Google reCAPTCHA verification failed', false );
872
+ }
873
+ } else {
874
+ return wpforo_phrase( 'Google reCAPTCHA data are not submitted', false );
875
+ }
876
+ }
877
+
878
+ public function rc_verify() {
879
+ if( ! wpforo_setting( 'recaptcha', 'post_editor' ) || ! wpforo_setting( 'recaptcha', 'topic_editor' ) ) {
880
+ if( ! wpforo_setting( 'recaptcha', 'post_editor' ) && in_array(
881
+ wpfval( $_POST, 'wpfaction' ),
882
+ [ 'post_add', 'post_edit' ],
883
+ true
884
+ ) ) {
885
+ return true;
886
+ } elseif( ! wpforo_setting( 'recaptcha', 'topic_editor' ) && in_array(
887
+ wpfval( $_POST, 'wpfaction' ),
888
+ [ 'topic_add', 'topic_edit' ],
889
+ true
890
+ ) ) {
891
+ return true;
892
+ }
893
+ }
894
+ $result = $this->rc_check();
895
+ if( $result === 'success' ) {
896
+ return true;
897
+ } else {
898
+ WPF()->notice->add( $result, 'error' );
899
+ wp_safe_redirect( wpforo_get_request_uri() );
900
+ exit();
901
+ }
902
+ }
903
+
904
+ public function rc_verify_wp_login( $user ) {
905
+ if( ! isset( $_POST['log'] ) && ! isset( $_POST['pwd'] ) ) return $user;
906
+ if( ! wpforo_setting( 'recaptcha', 'login_form' ) || ! wpforo_setting( 'recaptcha', 'wpf_login_form' ) ) {
907
+ if( ! wpfval( $_POST, 'wpforologin' ) && ! wpforo_setting( 'recaptcha', 'login_form' ) ) {
908
+ return $user;
909
+ } elseif( wpfval( $_POST, 'wpforologin' ) && ! wpforo_setting( 'recaptcha', 'wpf_login_form' ) ) {
910
+ return $user;
911
+ }
912
+ }
913
+ $errors = is_wp_error( $user ) ? $user : new WP_Error();
914
+ $result = $this->rc_check();
915
+ if( $result != 'success' ) {
916
+ $errors->add( 'wpforo-recaptcha-error', $result );
917
+ $user = is_wp_error( $user ) ? $user : $errors;
918
+ remove_filter( 'authenticate', 'wp_authenticate_username_password' );
919
+ remove_filter( 'authenticate', 'wp_authenticate_cookie' );
920
+ }
921
+
922
+ return $user;
923
+ }
924
+
925
+ public function rc_verify_wp_register( $errors = '' ) {
926
+ if( ! is_wp_error( $errors ) ) $errors = new WP_Error();
927
+ if( ! wpforo_setting( 'recaptcha', 'reg_form' ) || ! wpforo_setting( 'recaptcha', 'wpf_reg_form' ) ) {
928
+ if( ! wpfval( $_POST, 'wpfreg' ) && ! wpforo_setting( 'recaptcha', 'reg_form' ) ) {
929
+ return $errors;
930
+ } elseif( wpfval( $_POST, 'wpfreg' ) && ! wpforo_setting( 'recaptcha', 'wpf_reg_form' ) ) {
931
+ return $errors;
932
+ }
933
+ }
934
+ $result = $this->rc_check();
935
+ if( $result != 'success' ) {
936
+ $errors->add( 'wpforo-recaptcha-error', $result );
937
+ }
938
+
939
+ return $errors;
940
+ }
941
+
942
+ public function rc_verify_wp_lostpassword( $errors = '' ) {
943
+ if( ! is_wp_error( $errors ) ) $errors = new WP_Error();
944
+ if( ! wpforo_setting( 'recaptcha', 'lostpass_form' ) || ! wpforo_setting( 'recaptcha', 'wpf_lostpass_form' ) ) {
945
+ if( ! wpfval( $_POST, 'wpfororp' ) && ! wpforo_setting( 'recaptcha', 'lostpass_form' ) ) {
946
+ return;
947
+ } elseif( wpfval( $_POST, 'wpfororp' ) && ! wpforo_setting( 'recaptcha', 'wpf_lostpass_form' ) ) {
948
+ return;
949
+ }
950
+ }
951
+ $result = $this->rc_check();
952
+ if( $result !== 'success' ) {
953
+ if( isset( $_POST['wc_reset_password'] ) && isset( $_POST['_wp_http_referer'] ) ) {
954
+ //$errors->add('wpforo-recaptcha-error', $result);
955
+ //return $errors;
956
+ } else {
957
+ wp_die( $result, 'reCAPTCHA ERROR', [ 'back_link' => true ] );
958
+ }
959
+ }
960
+ }
961
+ }
classes/Actions.php ADDED
@@ -0,0 +1,2835 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ // Exit if accessed directly
6
+ if( ! defined( 'ABSPATH' ) ) exit();
7
+
8
+ class Actions {
9
+ /**
10
+ * wpForoAction constructor.
11
+ */
12
+ public function __construct() {
13
+ $this->init_hooks();
14
+ }
15
+
16
+ /**
17
+ * method for initializing all necessary hooks
18
+ */
19
+ public function init_hooks() {
20
+ add_action( 'wpforo_after_init_classes', function() { if( ! WPF()->is_installed() ) wpforo_activation(); }, 0 );
21
+
22
+ add_action( 'wpforo_after_init', [ $this, 'do_actions' ], 999 );
23
+ add_action( 'wpforo_action_user_delete', [ $this, 'user_delete' ] );
24
+ add_action( 'deleted_user', [ $this, 'deleted_user' ], 10, 2 );
25
+
26
+ add_action( 'wp_ajax_wpforo_profiles_default_cover_upload', [ $this, 'profiles_default_cover_upload' ] );
27
+ add_action( 'wp_ajax_wpforo_deactivate', [ $this, 'deactivate' ] );
28
+
29
+ if( ! wpforo_is_admin() ) {
30
+ add_action( 'wpforo_actions', function(){
31
+ if( wpfval($_REQUEST, 'wpfaction') === 'topic_add' ) unregister_post_type( 'topic' );
32
+ } );
33
+
34
+ add_action( 'wpforo_actions', [ $this, 'init_default_attach_hooks' ] );
35
+ add_action( 'wpforo_actions', [ $this, 'init_wp_emoji_hooks' ] );
36
+
37
+ add_action( 'wpforo_actions', [ $this, 'feed_rss2' ] );
38
+ add_action( 'wpforo_actions', [ $this, 'ucf_file_delete' ] );
39
+ add_action( 'wpforo_actions', [ $this, 'mark_all_read' ] );
40
+ add_action( 'wpforo_actions', [ $this, 'mark_notification_read' ] );
41
+
42
+ add_action( 'wpforo_action_registration', [ $this, 'registration' ] );
43
+ add_action( 'wpforo_action_login', [ $this, 'login' ] );
44
+ add_action( 'wpforo_action_lostpassword', [ $this, 'lostpassword' ] );
45
+ add_action( 'wpforo_action_resetpassword_form', [ $this, 'resetpassword_form' ] );
46
+ add_action( 'wpforo_action_resetpassword', [ $this, 'resetpassword' ] );
47
+ add_action( 'wpforo_action_profile_update', [ $this, 'profile_update' ] );
48
+ add_action( 'wpforo_action_cantlogin_contact', [ $this, 'cantlogin_contact' ] );
49
+
50
+ add_action( 'wpforo_action_topic_add', [ $this, 'topic_add' ] );
51
+ add_action( 'wpforo_action_topic_edit', [ $this, 'topic_edit' ] );
52
+ add_action( 'wpforo_action_topic_move', [ $this, 'topic_move' ] );
53
+ add_action( 'wpforo_action_topic_merge', [ $this, 'topic_merge' ] );
54
+ add_action( 'wpforo_action_topic_split', [ $this, 'topic_split' ] );
55
+
56
+ add_action( 'wpforo_action_post_add', [ $this, 'post_add' ] );
57
+ add_action( 'wpforo_action_post_edit', [ $this, 'post_edit' ] );
58
+
59
+ ## ajax actions ##
60
+ add_action( 'wp_ajax_wpforo_dissmiss_recaptcha_note', [ $this, 'dissmiss_recaptcha_note' ] );
61
+ add_action( 'wp_ajax_wpforo_acp_toggle', [ $this, 'acp_toggle' ] );
62
+ add_action( 'wp_ajax_wpforo_clear_all_notifications', [ $this, 'clear_all_notifications' ] );
63
+ add_action( 'wp_ajax_wpforo_profile_cover_upload', [ $this, 'profile_cover_upload' ] );
64
+ add_action( 'wp_ajax_wpforo_get_topic_head_more_info', [ $this, 'get_topic_head_more_info' ] );
65
+ add_action( 'wp_ajax_nopriv_wpforo_get_topic_head_more_info', [ $this, 'get_topic_head_more_info' ] );
66
+ add_action( 'wp_ajax_wpforo_get_topic_overview_chunk', [ $this, 'get_topic_overview_chunk' ] );
67
+ add_action( 'wp_ajax_nopriv_wpforo_get_topic_overview_chunk', [ $this, 'get_topic_overview_chunk' ] );
68
+ add_action( 'wp_ajax_wpforo_get_overview', [ $this, 'get_overview' ] );
69
+ add_action( 'wp_ajax_nopriv_wpforo_get_overview', [ $this, 'get_overview' ] );
70
+ add_action( 'wp_ajax_wpforo_user_ban', [ $this, 'user_ban_ajax' ] );
71
+ add_action( 'wp_ajax_wpforo_get_member_template', [ $this, 'get_member_template' ] );
72
+ add_action( 'wp_ajax_nopriv_wpforo_get_member_template', [ $this, 'get_member_template' ] );
73
+ add_action( 'wp_ajax_wpforo_search_existed_topics', [ $this, 'search_existed_topics' ] );
74
+ add_action( 'wp_ajax_nopriv_wpforo_search_existed_topics', [ $this, 'search_existed_topics' ] );
75
+ } else {
76
+ add_action( 'wpforo_actions', [ $this, 'check_dashboard_permissions' ], 1 );
77
+ add_action( 'wpforo_actions', [ $this, 'repair_lost_main_shortcode_page' ] );
78
+
79
+ add_action( 'wpforo_action_synch_user_profiles', [ $this, 'synch_user_profiles' ] );
80
+ add_action( 'wpforo_action_reset_user_cache', [ $this, 'reset_user_cache' ] );
81
+ add_action( 'wpforo_action_reset_forums_stats', [ $this, 'reset_forums_stats' ] );
82
+ add_action( 'wpforo_action_reset_topics_stats', [ $this, 'reset_topics_stats' ] );
83
+ add_action( 'wpforo_action_reset_users_stats', [ $this, 'reset_users_stats' ] );
84
+ add_action( 'wpforo_action_rebuild_threads', [ $this, 'rebuild_threads' ] );
85
+ add_action( 'wpforo_action_reset_phrase_cache', [ $this, 'reset_phrase_cache' ] );
86
+ add_action( 'wpforo_action_recrawl_phrases', [ $this, 'recrawl_phrases' ] );
87
+ add_action( 'wpforo_action_clean_up', [ $this, 'clean_up' ] );
88
+
89
+ add_action( 'wpforo_action_base_slugs_settings_save', [ $this, 'base_slugs_settings_save' ] );
90
+ add_action( 'wpforo_action_general_settings_save', [ $this, 'general_settings_save' ] );
91
+
92
+ add_action( 'wpforo_action_slugs_settings_save', [ $this, 'slugs_settings_save' ] );
93
+ add_action( 'wpforo_action_board_settings_save', [ $this, 'board_settings_save' ] );
94
+
95
+ add_action( 'wpforo_action_akismet_settings_save', [ $this, 'akismet_settings_save' ] );
96
+ add_action( 'wpforo_action_antispam_settings_save', [ $this, 'antispam_settings_save' ] );
97
+ add_action( 'wpforo_action_authorization_settings_save', [ $this, 'authorization_settings_save' ] );
98
+ add_action( 'wpforo_action_buddypress_settings_save', [ $this, 'buddypress_settings_save' ] );
99
+ add_action( 'wpforo_action_components_settings_save', [ $this, 'components_settings_save' ] );
100
+ add_action( 'wpforo_action_email_settings_save', [ $this, 'email_settings_save' ] );
101
+ add_action( 'wpforo_action_forums_settings_save', [ $this, 'forums_settings_save' ] );
102
+ add_action( 'wpforo_action_logging_settings_save', [ $this, 'logging_settings_save' ] );
103
+ add_action( 'wpforo_action_members_settings_save', [ $this, 'members_settings_save' ] );
104
+ add_action( 'wpforo_action_notifications_settings_save', [ $this, 'notifications_settings_save' ] );
105
+ add_action( 'wpforo_action_posting_settings_save', [ $this, 'posting_settings_save' ] );
106
+ add_action( 'wpforo_action_profiles_settings_save', [ $this, 'profiles_settings_save' ] );
107
+ add_action( 'wpforo_action_rating_settings_save', [ $this, 'rating_settings_save' ] );
108
+ add_action( 'wpforo_action_recaptcha_settings_save', [ $this, 'recaptcha_settings_save' ] );
109
+ add_action( 'wpforo_action_rss_settings_save', [ $this, 'rss_settings_save' ] );
110
+ add_action( 'wpforo_action_seo_settings_save', [ $this, 'seo_settings_save' ] );
111
+ add_action( 'wpforo_action_social_settings_save', [ $this, 'social_settings_save' ] );
112
+ add_action( 'wpforo_action_styles_settings_save', [ $this, 'styles_settings_save' ] );
113
+ add_action( 'wpforo_action_tags_settings_save', [ $this, 'tags_settings_save' ] );
114
+ add_action( 'wpforo_action_topics_settings_save', [ $this, 'topics_settings_save' ] );
115
+ add_action( 'wpforo_action_um_settings_save', [ $this, 'um_settings_save' ] );
116
+ add_action( 'wpforo_action_legal_settings_save', [ $this, 'legal_settings_save' ] );
117
+
118
+ add_action( 'wpforo_action_board_add', [ $this, 'board_add' ] );
119
+ add_action( 'wpforo_action_board_edit', [ $this, 'board_edit' ] );
120
+ add_action( 'wpforo_action_board_delete', [ $this, 'board_delete' ] );
121
+
122
+ add_action( 'wpforo_action_add_new_xml_translation', [ $this, 'add_new_xml_translation' ] );
123
+ add_action( 'wpforo_action_phrases_change_lang', [ $this, 'phrases_change_lang' ] );
124
+ add_action( 'wpforo_action_dashboard_options_save', [ $this, 'dashboard_options_save' ] );
125
+ add_action( 'wpforo_action_colors_css_download', [ $this, 'colors_css_download' ] );
126
+ // add_action( 'wpforo_action_cleanup_options_save', [ $this, 'cleanup_options_save' ] );
127
+ add_action( 'wpforo_action_misc_options_save', [ $this, 'misc_options_save' ] );
128
+ add_action( 'wpforo_action_legal_options_save', [ $this, 'legal_options_save' ] );
129
+ add_action( 'wpforo_action_delete_spam_file', [ $this, 'delete_spam_file' ] );
130
+ add_action( 'wpforo_action_delete_all_spam_files', [ $this, 'delete_all_spam_files' ] );
131
+ add_action( 'wpforo_action_database_update', [ $this, 'database_update' ] );
132
+
133
+ add_action( 'wpforo_action_forum_add', [ $this, 'forum_add' ] );
134
+ add_action( 'wpforo_action_forum_edit', [ $this, 'forum_edit' ] );
135
+ add_action( 'wpforo_action_forum_delete', [ $this, 'forum_delete' ] );
136
+ add_action( 'wpforo_action_forum_hierarchy_save', [ $this, 'forum_hierarchy_save' ] );
137
+
138
+ add_action( 'wpforo_action_dashboard_post_unapprove', [ $this, 'dashboard_post_unapprove' ] );
139
+ add_action( 'wpforo_action_dashboard_post_approve', [ $this, 'dashboard_post_approve' ] );
140
+ add_action( 'wpforo_action_dashboard_post_delete', [ $this, 'dashboard_post_delete' ] );
141
+ add_action( 'wpforo_action_bulk_moderation', [ $this, 'bulk_moderation' ] );
142
+
143
+ add_action( 'wpforo_action_phrase_add', [ $this, 'phrase_add' ] );
144
+ add_action( 'wpforo_action_phrase_edit_form', [ $this, 'phrase_edit_form' ] );
145
+ add_action( 'wpforo_action_phrase_edit', [ $this, 'phrase_edit' ] );
146
+
147
+ add_action( 'wpforo_action_user_ban', [ $this, 'user_ban' ] );
148
+ add_action( 'wpforo_action_user_unban', [ $this, 'user_unban' ] );
149
+ add_action( 'wpforo_action_user_activate', [ $this, 'user_activate' ] );
150
+ add_action( 'wpforo_action_user_deactivate', [ $this, 'user_deactivate' ] );
151
+ add_action( 'wpforo_action_bulk_members', [ $this, 'bulk_members' ] );
152
+
153
+ add_action( 'wpforo_action_usergroup_add', [ $this, 'usergroup_add' ] );
154
+ add_action( 'wpforo_action_usergroup_edit', [ $this, 'usergroup_edit' ] );
155
+ add_action( 'wpforo_action_usergroup_delete', [ $this, 'usergroup_delete' ] );
156
+ add_action( 'wpforo_action_default_groupid_change', [ $this, 'default_groupid_change' ] );
157
+ add_action( 'wpforo_action_usergroup_delete_form', [ $this, 'usergroup_delete_form' ] );
158
+
159
+ add_action( 'wpforo_action_access_add', [ $this, 'access_add' ] );
160
+ add_action( 'wpforo_action_access_edit', [ $this, 'access_edit' ] );
161
+ add_action( 'wpforo_action_access_delete', [ $this, 'access_delete' ] );
162
+
163
+ add_action( 'wpforo_action_theme_activate', [ $this, 'theme_activate' ] );
164
+ add_action( 'wpforo_action_theme_delete', [ $this, 'theme_delete' ] );
165
+
166
+ add_action( 'wpforo_action_update_addons_css', [ $this, 'update_addons_css' ] );
167
+ add_action( 'wpforo_action_dissmiss_poll_version_is_old', [ $this, 'dissmiss_poll_version_is_old' ] );
168
+
169
+ add_action( 'wpforo_action_uninstall', [ $this, 'uninstall' ] );
170
+ }
171
+ add_action( 'wpforo_action_reset_all_caches', [ $this, 'reset_all_caches' ] );
172
+ }
173
+
174
+ /**
175
+ * wpforo main actions doing place
176
+ */
177
+ public function do_actions() {
178
+ do_action( 'wpforo_actions' );
179
+ $wpforo_actions = array_unique( array_merge( (array) wpfval( $_POST, 'wpfaction' ), (array) wpfval( WPF()->GET, 'wpfaction' ) ) );
180
+ if( ! empty( $wpforo_actions ) ) {
181
+ foreach( $wpforo_actions as $wpforo_action ) {
182
+ $wpforo_action = sanitize_title( $wpforo_action );
183
+ do_action( "wpforo_action_{$wpforo_action}" );
184
+ }
185
+ }
186
+ do_action( 'wpforo_actions_end' );
187
+ }
188
+
189
+ /**
190
+ * init wpforo default attachments system when wpforo advanced attachments addon has not exists
191
+ */
192
+ public function init_default_attach_hooks() {
193
+ add_action( 'delete_attachment', 'wpforo_delete_attachment', 10 );
194
+ if( has_action( 'wpforo_topic_form_extra_fields_after', [ WPF()->tpl, 'add_default_attach_input' ] ) ) {
195
+ add_filter( 'wpforo_add_topic_data_filter', 'wpforo_add_default_attachment' );
196
+ add_filter( 'wpforo_edit_topic_data_filter', 'wpforo_add_default_attachment' );
197
+ add_filter( 'wpforo_add_post_data_filter', 'wpforo_add_default_attachment' );
198
+ add_filter( 'wpforo_edit_post_data_filter', 'wpforo_add_default_attachment' );
199
+ add_filter( 'wpforo_body_text_filter', 'wpforo_default_attachments_filter' );
200
+ }
201
+ }
202
+
203
+ /**
204
+ * init wp emojis when wpforo emoticons addon has not exists
205
+ */
206
+ public function init_wp_emoji_hooks() {
207
+ if( ! class_exists( 'wpForoSmiles' ) ) {
208
+ add_filter( 'wpforo_body_text_filter', 'wp_encode_emoji', 9 );
209
+ add_filter( 'wpforo_body_text_filter', 'convert_smilies' );
210
+ }
211
+ }
212
+
213
+ /**
214
+ * get request_uri redirect to url with concatenation of &can_do=do
215
+ * @return bool true if you can do action now | false if you can not do action now
216
+ */
217
+ private function can_do() {
218
+ if( wpfval( $_GET, 'can_do' ) === 'do' ) return true;
219
+
220
+ $refresh_url = preg_replace( '#&can_do=?[^=?&\r\n]*#isu', '', wpforo_get_request_uri() );
221
+ $refresh_url .= '&can_do=do';
222
+ header( "refresh:0.1;url=" . $refresh_url );
223
+
224
+ add_filter( 'wpforo_admin_loading', '__return_true' );
225
+
226
+ return false;
227
+ }
228
+
229
+ /**
230
+ * @return string $u_action return union bulk action
231
+ */
232
+ private function get_current_bulk_action() {
233
+ $u_action = '';
234
+ if( ! empty( $_GET['action'] ) && $_GET['action'] !== '-1' ) {
235
+ $u_action = sanitize_textarea_field( $_GET['action'] );
236
+ } elseif( ! empty( $_GET['action2'] ) && $_GET['action2'] !== '-1' ) {
237
+ $u_action = sanitize_textarea_field( $_GET['action2'] );
238
+ }
239
+
240
+ return $u_action;
241
+ }
242
+
243
+ /**
244
+ * catch if rss url show rss feed for given arguments
245
+ */
246
+ public function feed_rss2() {
247
+ if( wpfval( WPF()->GET, 'type' ) === 'rss2' ) {
248
+ $forum_rss_items = apply_filters( 'wpforo_forum_feed_limit', 10 );
249
+ $topic_rss_items = apply_filters( 'wpforo_topic_feed_limit', 10 );
250
+
251
+ $forumid = intval( wpfval( WPF()->GET, 'forum' ) );
252
+ if( ! $forumid ) {
253
+ $forum = [];
254
+ $forum['forumurl'] = wpforo_home_url();
255
+ $forum['title'] = '';
256
+ } elseif( $forum = wpforo_forum( $forumid ) ) {
257
+ $forum['forumurl'] = $forum['url'];
258
+ }
259
+
260
+ if( wpfval( WPF()->GET, 'topic' ) ) {
261
+ $topicid = intval( WPF()->GET['topic'] );
262
+ if( ! $topicid ) {
263
+ $posts = WPF()->post->get_posts( [
264
+ 'row_count' => $topic_rss_items,
265
+ 'orderby' => '`created` DESC, `postid` DESC',
266
+ 'check_private' => true,
267
+ ] );
268
+ $topic['title'] = '';
269
+ $topic['topicurl'] = wpforo_home_url();
270
+ } else {
271
+ $topic = wpforo_topic( $topicid );
272
+ $topic['topicurl'] = ( wpfval( $topic, 'url' ) ) ? $topic['url'] : WPF()->topic->get_topic_url( $topicid );
273
+ $posts = WPF()->post->get_posts( [
274
+ 'topicid' => $topicid,
275
+ 'row_count' => $topic_rss_items,
276
+ 'orderby' => '`created` DESC, `postid` DESC',
277
+ 'check_private' => true,
278
+ ] );
279
+ }
280
+ foreach( $posts as $key => $post ) {
281
+ $member = wpforo_member( $post );
282
+ $posts[ $key ]['description'] = wpforo_text( trim( strip_tags( $post['body'] ) ), 190, false );
283
+ $posts[ $key ]['content'] = trim( $post['body'] );
284
+ $posts[ $key ]['posturl'] = WPF()->post->get_post_url( $post['postid'] );
285
+ $posts[ $key ]['author'] = $member['display_name'];
286
+ }
287
+ WPF()->feed->rss2_topic( $forum, $topic, $posts );
288
+ } else {
289
+ if( ! $forumid ) {
290
+ $topics = WPF()->topic->get_topics( [
291
+ 'row_count' => $forum_rss_items,
292
+ 'orderby' => 'created',
293
+ 'order' => 'DESC',
294
+ ] );
295
+ } else {
296
+ $topics = WPF()->topic->get_topics( [
297
+ 'forumid' => $forumid,
298
+ 'row_count' => $forum_rss_items,
299
+ 'orderby' => 'created',
300
+ 'order' => 'DESC',
301
+ ] );
302
+ }
303
+ foreach( $topics as $key => $topic ) {
304
+ $post = wpforo_post( $topic['first_postid'] );
305
+ $member = wpforo_member( $topic );
306
+ $topics[ $key ]['description'] = wpforo_text( trim( strip_tags( $post['body'] ) ), 190, false );
307
+ $topics[ $key ]['content'] = trim( $post['body'] );
308
+ $topics[ $key ]['topicurl'] = WPF()->topic->get_topic_url( $topic['topicid'] );
309
+ $topics[ $key ]['author'] = $member['display_name'];
310
+ }
311
+ WPF()->feed->rss2_forum( $forum, $topics );
312
+ }
313
+ exit();
314
+ }
315
+ }
316
+
317
+ /**
318
+ * ucf_file_delete delete /wp-content/uploads/UCFFILENAME
319
+ */
320
+ public function ucf_file_delete() {
321
+ if( wpfval( WPF()->GET, 'foro_f' ) && wpfval( WPF()->GET, 'foro_u' ) && wpfval( WPF()->GET, 'foro_n' ) ) {
322
+ if( wp_verify_nonce( WPF()->GET['foro_n'], 'wpforo_delete_profile_field' ) ) {
323
+ $userid = intval( WPF()->GET['foro_u'] );
324
+ $field = sanitize_title( WPF()->GET['foro_f'] );
325
+ if( $file = WPF()->member->get_custom_field( $userid, $field ) ) {
326
+ $file = WPF()->folders['wp_upload']['dir'] . DIRECTORY_SEPARATOR . ltrim( wpforo_fix_dir_sep( $file ), DIRECTORY_SEPARATOR );
327
+ $result = WPF()->member->update_custom_field( $userid, $field, '' );
328
+ if( $result ) {
329
+ @unlink( $file );
330
+ WPF()->phrase->clear_cache();
331
+ WPF()->notice->add( 'Deleted Successfully!', 'success' );
332
+ } else {
333
+ WPF()->notice->clear();
334
+ WPF()->notice->add( 'Sorry, this file cannot be deleted', 'error' );
335
+ }
336
+ }
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * mark all bold forum topics as read
343
+ */
344
+ public function mark_all_read() {
345
+ if( wpfval( WPF()->GET, 'foro' ) === 'allread' ) {
346
+ if( wpfval( WPF()->GET, 'foro_n' ) && wp_verify_nonce( WPF()->GET['foro_n'], 'wpforo_mark_all_read' ) ) {
347
+ WPF()->log->mark_all_read();
348
+ $current_url = wpforo_get_request_uri();
349
+ $current_url = strtok( $current_url, '?' );
350
+ wp_safe_redirect( $current_url );
351
+ exit();
352
+ }
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Open/Close Frontend Admin CPanel
358
+ */
359
+ public function acp_toggle() {
360
+ $toggle_status = wpfval( $_POST, 'toggle_status' );
361
+ if( in_array( $toggle_status, [ 'open', 'close' ] ) ) {
362
+ update_user_meta( WPF()->current_userid, 'wpf-acp-toggle', $toggle_status );
363
+ wp_send_json_success();
364
+ } else {
365
+ wp_send_json_error();
366
+ }
367
+ }
368
+
369
+ /**
370
+ * set a notification read
371
+ */
372
+ public function mark_notification_read() {
373
+ if( wpfval( WPF()->GET, '_nread' ) && is_user_logged_in() ) {
374
+ if( wpfval( WPF()->GET, 'foro_n' ) && wp_verify_nonce( WPF()->GET['foro_n'], 'wpforo_mark_notification_read' ) ) {
375
+ $id = intval( WPF()->GET['_nread'] );
376
+ WPF()->activity->read_notification( $id );
377
+ $current_url = wpforo_get_request_uri();
378
+ $current_url = strtok( $current_url, '?' );
379
+ wp_safe_redirect( $current_url );
380
+ exit();
381
+ }
382
+ }
383
+ }
384
+
385
+ /**
386
+ * clear all notifications
387
+ */
388
+ public function clear_all_notifications() {
389
+ if( wpfval( $_POST, 'foro_n' ) && wp_verify_nonce( $_POST['foro_n'], 'wpforo_clear_notifications' ) ) {
390
+ WPF()->activity->clear_notifications();
391
+ echo WPF()->activity->get_no_notifications_html();
392
+ }
393
+ exit();
394
+ }
395
+
396
+ public function profile_cover_upload() {
397
+ $r = false;
398
+ if( ($image_blob = wpfval( $_POST, 'image_blob' )) && WPF()->current_object['user'] ){
399
+ if( $file_content = file_get_contents($image_blob) ){
400
+ $file_basename = WPF()->current_object['user']['user_login'] . '_' . WPF()->current_object['user']['userid'] . '.jpg';
401
+ $file_dir = WPF()->folders['covers']['dir'] . DIRECTORY_SEPARATOR . $file_basename;
402
+ $file_url = WPF()->folders['covers']['url//'] . '/' . $file_basename;
403
+ if( file_put_contents($file_dir, $file_content) ){
404
+ $r = WPF()->member->update_profile_field( WPF()->current_object['user']['userid'], 'cover', $file_url );
405
+ }
406
+ }
407
+ }
408
+ if( $r !== false ){
409
+ wp_send_json_success();
410
+ }else{
411
+ wp_send_json_error();
412
+ }
413
+ }
414
+
415
+ public function profiles_default_cover_upload() {
416
+ $r = false;
417
+ if( $image_blob = wpfval( $_POST, 'image_blob' ) ){
418
+ if( $file_content = file_get_contents($image_blob) ){
419
+ $file_basename = 'profiles_custom_default_cover.jpg';
420
+ $file_dir = WPF()->folders['covers']['dir'] . DIRECTORY_SEPARATOR . $file_basename;
421
+ $file_url = WPF()->folders['covers']['url//'] . '/' . $file_basename;
422
+ if( file_put_contents($file_dir, $file_content) ){
423
+ WPF()->settings->profiles['default_cover'] = $file_url;
424
+ $r = wpforo_update_option( 'wpforo_profiles', WPF()->settings->profiles );
425
+ }
426
+ }
427
+ }
428
+ if( $r !== false ){
429
+ wp_send_json_success();
430
+ }else{
431
+ wp_send_json_error();
432
+ }
433
+ }
434
+
435
+ public function get_topic_head_more_info() {
436
+ if( $topicid = wpforo_bigintval( wpfval( $_POST, 'topicid' ) ) ){
437
+ wp_send_json_success( [ 'html' => wpforo_topic_active_participants( $topicid ) . wpforo_topic_overview( $topicid ) ] );
438
+ }
439
+
440
+ wp_send_json_error();
441
+ }
442
+
443
+ public function get_topic_overview_chunk() {
444
+ if( $topicid = wpforo_bigintval( wpfval( $_POST, 'topicid' ) ) ){
445
+ if( !( $chunksize = (int) wpfval( $_POST, 'chunksize' ) ) ) $chunksize = 5;
446
+ $offset = (int) wpfval( $_POST, 'offset' );
447
+
448
+ wp_send_json_success( wpforo_get_topic_overview_chunk( $topicid, $chunksize, $offset ) );
449
+ }
450
+
451
+ wp_send_json_error();
452
+ }
453
+
454
+ public function get_overview() {
455
+ if( $postid = wpforo_bigintval( wpfval( $_POST, 'postid' ) ) ){
456
+ if( $post = wpforo_post( $postid ) ){
457
+ wp_send_json_success(
458
+ [
459
+ 'title' => '<i class="fas fa-user"></i> &nbsp;' . wpforo_phrase( 'Posted by ', false )
460
+ . ' ' . wpforo_member_link(wpforo_member($post), '', 20, '', false)
461
+ . '&nbsp;&bullet;&nbsp;'
462
+ . ' ' . wpforo_date($post['created'], 'ago', false),
463
+ 'content' => trim( $post['body'] ),
464
+ ]
465
+ );
466
+ }
467
+ }
468
+
469
+ wp_send_json_error();
470
+ }
471
+
472
+ /**
473
+ * registration form submit action
474
+ */
475
+ public function registration() {
476
+ if( ! empty( $_POST['wpfreg'] ) ) {
477
+ wpforo_verify_form( 'wpforo_user_register' );
478
+ if( $userid = WPF()->member->create( $_POST ) ) {
479
+ if( wpforo_setting( 'authorization', 'redirect_url_after_register' ) ) {
480
+ $redirect_url = wpforo_setting( 'authorization', 'redirect_url_after_register' );
481
+ } elseif( ( $redirect_to = wpfval( $_GET, 'redirect_to' ) ) && wpforo_is_url_internal( urldecode( $redirect_to ) ) ) {
482
+ $redirect_url = urldecode( $redirect_to );
483
+ } elseif( is_wpforo_url() ) {
484
+ $redirect_url = preg_replace( '#\?.*$#is', '', wpforo_get_request_uri() );
485
+ } else {
486
+ $redirect_url = ( wpforo_setting( 'authorization', 'user_register_email_confirm' ) ? wpforo_home_url() : WPF()->member->get_profile_url( $userid, 'account' ) );
487
+ }
488
+
489
+ wp_safe_redirect( $redirect_url );
490
+ exit();
491
+ }
492
+ }
493
+ }
494
+
495
+ /**
496
+ * login form submit action
497
+ */
498
+ public function login() {
499
+ wpforo_verify_form( 'login' );
500
+ if( isset( $_POST['wpforologin'] ) && isset( $_POST['log'] ) && isset( $_POST['pwd'] ) ) {
501
+ if( ! is_wp_error( $user = wp_signon() ) ) {
502
+ $wpf_login_times = intval( get_user_meta( $user->ID, '_wpf_login_times', true ) );
503
+ if( isset( $user->ID ) && $wpf_login_times >= 1 ) {
504
+ $name = ( isset( $user->data->display_name ) ) ? $user->data->display_name : '';
505
+ WPF()->notice->add( 'Welcome back %s!', 'success', $name );
506
+ } else {
507
+ WPF()->notice->add( 'Welcome to our Community!', 'success' );
508
+ }
509
+ $wpf_login_times ++;
510
+ update_user_meta( $user->ID, '_wpf_login_times', $wpf_login_times );
511
+ if( wpforo_setting( 'authorization', 'redirect_url_after_login' ) ) {
512
+ $redirect_url = wpforo_setting( 'authorization', 'redirect_url_after_login' );
513
+ } elseif( ( $redirect_to = wpfval( $_GET, 'redirect_to' ) ) && wpforo_is_url_internal( urldecode( $redirect_to ) ) ) {
514
+ $redirect_url = urldecode( $redirect_to );
515
+ } elseif( is_wpforo_url() ) {
516
+ $redirect_url = preg_replace( '#\?.*$#is', '', wpforo_get_request_uri() );
517
+ } else {
518
+ $redirect_url = wpforo_home_url();
519
+ }
520
+ wp_safe_redirect( $redirect_url );
521
+ } else {
522
+ $args = [];
523
+ foreach( $user->errors as $u_err ) $args[] = $u_err[0];
524
+ WPF()->notice->add( $args, 'error' );
525
+ wp_safe_redirect( wpforo_get_request_uri() );
526
+ }
527
+ exit();
528
+ }
529
+ }
530
+
531
+ public function lostpassword() {
532
+ wpforo_verify_form( 'lostpassword' );
533
+ $redirect_url = wp_get_raw_referer();
534
+ if( wpfval( $_POST, 'user_login' ) ) {
535
+ $errors = retrieve_password();
536
+ if( is_wp_error( $errors ) ) {
537
+ $redirect_url = wpforo_lostpassword_url();
538
+ WPF()->notice->add( implode( ',', $errors->get_error_codes() ), 'error' );
539
+ } else {
540
+ $redirect_url = wpforo_login_url();
541
+ WPF()->notice->add( 'Email has been sent', 'success' );
542
+ }
543
+ }
544
+
545
+ wp_safe_redirect( $redirect_url );
546
+ exit();
547
+ }
548
+
549
+ public function resetpassword_form() {
550
+ $rp_key = sanitize_text_field( wp_unslash( $_REQUEST['rp_key'] ) );
551
+ $rp_login = sanitize_user( wp_unslash( $_REQUEST['rp_login'] ) );
552
+ $user = check_password_reset_key( $rp_key, $rp_login );
553
+ if( ! $user || is_wp_error( $user ) ) {
554
+ if( $user && $user->get_error_code() === 'expired_key' ) {
555
+ WPF()->notice->add( 'The key is expired', 'error' );
556
+ } else {
557
+ WPF()->notice->add( 'The key is invalid', 'error' );
558
+ }
559
+ wp_safe_redirect( wpforo_login_url() );
560
+ exit();
561
+ }
562
+ }
563
+
564
+ public function resetpassword() {
565
+ $this->resetpassword_form();
566
+
567
+ $pass1 = wpfval( $_POST, 'pass1' );
568
+ $pass2 = wpfval( $_POST, 'pass2' );
569
+
570
+ if( ! $pass1 ) {
571
+ WPF()->notice->add( 'The password reset empty', 'error' );
572
+ wp_safe_redirect( wp_get_raw_referer() );
573
+ exit();
574
+ }
575
+
576
+ if( strlen( $pass1 ) < WPF()->member->pass_min_length || strlen( $pass1 ) > WPF()->member->pass_max_length ) {
577
+ WPF()->notice->add( 'Password length must be between %d characters and %d characters.', 'error', [
578
+ WPF()->member->pass_min_length,
579
+ WPF()->member->pass_max_length,
580
+ ] );
581
+ wp_safe_redirect( wp_get_raw_referer() );
582
+ exit();
583
+ }
584
+
585
+ if( $pass1 !== $pass2 ) {
586
+ WPF()->notice->add( 'The password reset mismatch', 'error' );
587
+ wp_safe_redirect( wp_get_raw_referer() );
588
+ exit();
589
+ }
590
+
591
+ $rp_login = sanitize_user( wp_unslash( $_REQUEST['rp_login'] ) );
592
+ reset_password( get_user_by( 'login', $rp_login ), $pass1 );
593
+ wp_signon( [ 'user_login' => $rp_login, 'user_password' => $pass1 ] );
594
+
595
+ WPF()->notice->add( 'The password has been changed', 'success' );
596
+ wp_safe_redirect( wpforo_home_url() );
597
+ exit();
598
+ }
599
+
600
+ public function logout() {
601
+ wp_logout();
602
+ $redirect_url = wpforo_home_url();
603
+ if( ( $redirect_to = wpfval( $_GET, 'redirect_to' ) ) && wpforo_is_url_internal( urldecode( $redirect_to ) ) ) {
604
+ $redirect_url = urldecode( $redirect_to );
605
+ if( strpos( $redirect_url, 'lostpassword' ) !== false ) $redirect_url = wpforo_login_url();
606
+ }
607
+ wp_safe_redirect( $redirect_url );
608
+ exit();
609
+ }
610
+
611
+ /**
612
+ * profile_update form submit action
613
+ */
614
+ public function profile_update() {
615
+ if( wpfval( $_POST, 'member', 'userid' ) ) {
616
+ wpforo_verify_form();
617
+ $uid = intval( $_POST['member']['userid'] );
618
+ if( ! ( $uid === WPF()->current_userid || ( WPF()->usergroup->can( 'em' ) && WPF()->perm->user_can_manage_user( WPF()->current_userid, $uid ) ) ) ) {
619
+ WPF()->notice->clear();
620
+ WPF()->notice->add( 'Permission denied', 'error' );
621
+ wp_safe_redirect( wpforo_get_request_uri() );
622
+ exit();
623
+ }
624
+ if( WPF()->member->update( $_POST ) ) {
625
+ if( $profile_url = WPF()->member->get_profile_url( $uid, 'account', false ) ) {
626
+ wp_safe_redirect( $profile_url );
627
+ exit();
628
+ }
629
+ }
630
+ }
631
+
632
+ wp_safe_redirect( wpforo_get_request_uri() );
633
+ exit();
634
+ }
635
+
636
+ public function cantlogin_contact() {
637
+ if( wpforo_setting( 'authorization', 'manually_approval_contact_form' ) && ($msg = wpfval( $_POST, 'msg' )) ){
638
+ $admin_emails = wpforo_setting( 'email', 'admin_emails' );
639
+ $admin_email = wpfval($admin_emails, 0);
640
+ $sbj = wpforo_phrase( 'Request for account approval', false ) . ' ( '. wpfval( $_POST, 'user_login' ) .' )';
641
+
642
+ add_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type', 999 );
643
+ if( @wpforo_send_email( $admin_email, $sbj, $msg, wpforo_admin_mail_headers() ) ) {
644
+ WPF()->notice->add( 'Message has been sent', 'success' );
645
+ } else {
646
+ WPF()->notice->add( 'Can\'t send report email', 'error' );
647
+ }
648
+ remove_filter( 'wp_mail_content_type', 'wpforo_set_html_content_type' );
649
+ }
650
+
651
+ wp_safe_redirect( wpforo_home_url() );
652
+ exit();
653
+ }
654
+
655
+ /**
656
+ * topic_add form submit action
657
+ */
658
+ public function topic_add() {
659
+ wpforo_verify_form();
660
+ $args = $_REQUEST['thread'];
661
+ $args['postmetas'] = (array) wpfval( $_REQUEST, 'data' );
662
+ if( $topicid = WPF()->topic->add( $args ) ) {
663
+ wp_safe_redirect( WPF()->topic->get_topic_url( $topicid ) );
664
+ exit();
665
+ }
666
+ wp_safe_redirect( wpforo_get_request_uri() );
667
+ exit();
668
+ }
669
+
670
+ /**
671
+ * topic_edit form submit action
672
+ */
673
+ public function topic_edit() {
674
+ wpforo_verify_form();
675
+ $args = $_REQUEST['thread'];
676
+ $args['postmetas'] = (array) wpfval( $_REQUEST, 'data' );
677
+ if( $topicid = WPF()->topic->edit( $args ) ) {
678
+ wp_safe_redirect( WPF()->topic->get_topic_url( $topicid ) );
679
+ exit();
680
+ }
681
+ wp_safe_redirect( wpforo_get_request_uri() );
682
+ exit();
683
+ }
684
+
685
+ /**
686
+ * post_add form submit action
687
+ */
688
+ public function post_add() {
689
+ wpforo_verify_form();
690
+ $args = $_REQUEST['post'];
691
+ $args['postmetas'] = (array) wpfval( $_REQUEST, 'data' );
692
+ if( $postid = WPF()->post->add( $args ) ) {
693
+ wp_safe_redirect( WPF()->post->get_post_url( $postid ) );
694
+ exit();
695
+ }
696
+ wp_safe_redirect( wpforo_get_request_uri() );
697
+ exit();
698
+ }
699
+
700
+ /**
701
+ * post_edit form submit action
702
+ */
703
+ public function post_edit() {
704
+ wpforo_verify_form();
705
+ $args = $_REQUEST['post'];
706
+ $args['postmetas'] = (array) wpfval( $_REQUEST, 'data' );
707
+ if( $postid = WPF()->post->edit( $args ) ) {
708
+ wp_safe_redirect( WPF()->post->get_post_url( $postid ) );
709
+ exit();
710
+ }
711
+ wp_safe_redirect( wpforo_get_request_uri() );
712
+ exit();
713
+ }
714
+
715
+ /**
716
+ * topic_move form submit action
717
+ */
718
+ public function topic_move() {
719
+ if( ! empty( $_POST['topic_move'] ) ) {
720
+ wpforo_verify_form();
721
+ $topicid = intval( wpfval( $_POST['topic_move'], 'topicid' ) );
722
+ $forumid = intval( wpfval( $_POST['topic_move'], 'forumid' ) );
723
+ if( $topicid && $forumid ) {
724
+ WPF()->topic->move( $topicid, $forumid );
725
+ wp_safe_redirect( WPF()->topic->get_topic_url( $topicid ) );
726
+ exit();
727
+ }
728
+ }
729
+
730
+ wp_safe_redirect( wpforo_get_request_uri() );
731
+ exit();
732
+ }
733
+
734
+ /**
735
+ * topic_merge form submit action
736
+ */
737
+ public function topic_merge() {
738
+ wpforo_verify_form();
739
+ $redirect_to = wpforo_get_request_uri();
740
+ if( WPF()->current_object['topic'] && ! empty( $_POST['wpforo'] ) && ! empty( $_POST['wpforo']['target_topic_url'] ) ) {
741
+ $target_slug = wpforo_get_topic_slug_from_url( esc_url( $_POST['wpforo']['target_topic_url'] ) );
742
+ if( ! is_null( $target_slug ) && $target = WPF()->topic->get_topic( $target_slug ) ) {
743
+ $append = ( empty( $_POST['wpforo']['update_date_and_append'] ) ? 0 : 1 );
744
+ $to_target_title = ( empty( $_POST['wpforo']['to_target_title'] ) ? 0 : 1 );
745
+
746
+ if( WPF()->topic->merge( $target, WPF()->current_object['topic'], [], $to_target_title, $append ) ) {
747
+ $redirect_to = WPF()->topic->get_topic_url( $target );
748
+ }
749
+ } else {
750
+ WPF()->notice->add( 'Target Topic not found', 'error' );
751
+ }
752
+ }
753
+ wp_safe_redirect( $redirect_to );
754
+ exit();
755
+ }
756
+
757
+ /**
758
+ * topic_split form submit action
759
+ */
760
+ public function topic_split() {
761
+ wpforo_verify_form();
762
+ $redirect_to = wpforo_get_request_uri();
763
+ if( WPF()->current_object['topic'] && ! empty( $_POST['wpforo'] ) ) {
764
+ if( ! empty( $_POST['wpforo']['create_new'] ) ) {
765
+ $args = [
766
+ 'title' => sanitize_text_field( $_POST['wpforo']['new_topic_title'] ),
767
+ 'forumid' => intval( $_POST['wpforo']['new_topic_forumid'] ),
768
+ 'postids' => array_map( 'intval', $_POST['wpforo']['posts'] ),
769
+ ];
770
+ $to_target_title = ( empty( $_POST['wpforo']['to_target_title'] ) ? 0 : 1 );
771
+ if( $topicid = WPF()->topic->split( $args, $to_target_title ) ) {
772
+ $redirect_to = WPF()->topic->get_topic_url( $topicid );
773
+ }
774
+ } else {
775
+ if( ! empty( $_POST['wpforo']['target_topic_url'] ) && ! empty( $_POST['wpforo']['posts'] ) ) {
776
+ $target_slug = wpforo_get_topic_slug_from_url( esc_url( $_POST['wpforo']['target_topic_url'] ) );
777
+ if( ! is_null( $target_slug ) && $target = WPF()->topic->get_topic( $target_slug ) ) {
778
+ $append = ( empty( $_POST['wpforo']['update_date_and_append'] ) ? 0 : 1 );
779
+ $to_target_title = ( empty( $_POST['wpforo']['to_target_title'] ) ? 0 : 1 );
780
+ $postids = array_map( 'intval', $_POST['wpforo']['posts'] );
781
+ if( WPF()->topic->merge( $target, WPF()->current_object['topic'], $postids, $to_target_title, $append ) ) {
782
+ $redirect_to = WPF()->topic->get_topic_url( $target );
783
+ }
784
+ } else {
785
+ WPF()->notice->add( 'Target Topic not found', 'error' );
786
+ }
787
+ }
788
+ }
789
+ }
790
+ wp_safe_redirect( $redirect_to );
791
+ exit();
792
+ }
793
+
794
+ /**
795
+ * board add action
796
+ */
797
+ public function board_add() {
798
+ check_admin_referer( 'wpforo-board-add' );
799
+ if( $board = (array) wpfval( $_POST, 'board' ) ) {
800
+ if( ! ( $board['locale'] = trim( $board['locale'] ) ) ) $board['locale'] = 'en_US';
801
+ if( $boardid = WPF()->board->add( $board ) ) {
802
+ // Handle translation installation.
803
+ if( (int) $board['status'] && $board['locale'] && current_user_can( 'install_languages' ) ) {
804
+ require_once ABSPATH . 'wp-admin/includes/file.php';
805
+ require_once ABSPATH . 'wp-admin/includes/translation-install.php';
806
+ if( wp_can_install_language_pack() ) wp_download_language_pack( $board['locale'] );
807
+ }
808
+
809
+ if( ( $board = WPF()->board->_get_board( $boardid ) ) && $board['status'] ) {
810
+ wp_safe_redirect( admin_url( 'admin.php?page=wpforo-' . $boardid . '-settings' ) );
811
+ exit();
812
+ }
813
+ }
814
+ }
815
+
816
+ wp_safe_redirect( admin_url( 'admin.php?page=wpforo-boards' ) );
817
+ exit();
818
+ }
819
+
820
+ /**
821
+ * board edit action
822
+ */
823
+ public function board_edit() {
824
+ check_admin_referer( 'wpforo-board-edit' );
825
+ if( $board = (array) wpfval( $_POST, 'board' ) ) {
826
+ $boardid = (int) wpfval( $board, 'boardid' );
827
+ if( ! ( $board['locale'] = trim( $board['locale'] ) ) ) $board['locale'] = 'en_US';
828
+ if( WPF()->board->edit( $board, $boardid ) ) {
829
+ if( (int) $board['status'] && ! is_wpforo_multiboard() ){
830
+ wp_update_post(
831
+ [
832
+ 'ID' => $board['pageid'],
833
+ 'post_name' => $board['slug'],
834
+ ]
835
+ );
836
+ }
837
+ // Handle translation installation.
838
+ if( (int) $board['status'] && $board['locale'] && current_user_can( 'install_languages' ) ) {
839
+ require_once ABSPATH . 'wp-admin/includes/file.php';
840
+ require_once ABSPATH . 'wp-admin/includes/translation-install.php';
841
+ if( wp_can_install_language_pack() ) wp_download_language_pack( $board['locale'] );
842
+ }
843
+ }
844
+ }
845
+
846
+ wp_safe_redirect( admin_url( 'admin.php?page=wpforo-boards' ) );
847
+ exit();
848
+ }
849
+
850
+ /**
851
+ * board delete action
852
+ */
853
+ public function board_delete() {
854
+ if( ( $boardid = (int) wpfval( $_GET, 'boardid' ) ) && $boardid === WPF()->board->get_current( 'boardid' ) ) {
855
+ check_admin_referer( 'wpforo-board-delete-' . $boardid );
856
+ wpforo_board_uninstall( $boardid );
857
+ }
858
+
859
+ wp_safe_redirect( admin_url( 'admin.php?page=wpforo-boards' ) );
860
+ exit();
861
+ }
862
+
863
+ /**
864
+ * action to synchronize wp_users to wp_wpforo_profiles
865
+ */
866
+ public function synch_user_profiles() {
867
+ check_admin_referer( 'wpforo_synch_user_profiles' );
868
+
869
+ if( ! current_user_can( 'administrator' ) ) {
870
+ WPF()->notice->add( 'Permission denied', 'error' );
871
+ wp_safe_redirect( admin_url() );
872
+ exit();
873
+ }
874
+
875
+ if( $this->can_do() ) {
876
+ wpforo_set_max_execution_time();
877
+ wp_raise_memory_limit();
878
+
879
+ if( WPF()->member->synchronize_users( apply_filters( 'wpforo_rebuild_per_request', 200 ) ) ) {
880
+ WPF()->member->clear_db_cache();
881
+ wpforo_clean_cache();
882
+ WPF()->notice->add( 'Synched Successfully!', 'success' );
883
+ wp_safe_redirect( admin_url( 'admin.php?page=wpforo-overview' ) );
884
+ } else {
885
+ wp_safe_redirect( htmlspecialchars_decode( wp_nonce_url( admin_url( 'admin.php?page=wpforo-overview&wpfaction=synch_user_profiles' ), 'wpforo_synch_user_profiles' ) ) );
886
+ }
887
+ exit();
888
+ }
889
+ }
890
+
891
+ /**
892
+ * reset user caches
893
+ */
894
+ public function reset_user_cache() {
895
+ check_admin_referer( 'wpforo_reset_user_cache' );
896
+
897
+ if( ! current_user_can( 'administrator' ) ) {
898
+ WPF()->notice->add( 'Permission denied', 'error' );
899
+ wp_safe_redirect( admin_url() );
900
+ exit();
901
+ }
902
+
903
+ wpforo_set_max_execution_time();
904
+ wp_raise_memory_limit();
905
+
906
+ WPF()->member->clear_db_cache();
907
+ WPF()->notice->add( 'Deleted Successfully!', 'success' );
908
+
909
+ wp_safe_redirect( admin_url( 'admin.php?page=wpforo-overview' ) );
910
+ exit();
911
+ }
912
+
913
+ /**
914
+ * rebuild forums statistics first|last posts etc.
915
+ */
916
+ public function reset_forums_stats() {
917
+ check_admin_referer( 'wpforo_reset_forums_stat' );
918
+
919
+ if( ! current_user_can( 'administrator' ) ) {
920
+ WPF()->notice->add( 'Permission denied', 'error' );
921
+ wp_safe_redirect( admin_url() );
922
+ exit();
923
+ }
924
+
925
+ wpforo_set_max_execution_time();
926
+ wp_raise_memory_limit();
927
+
928
+ $forumids = WPF()->db->get_col( "SELECT `forumid` FROM " . WPF()->tables->forums . " WHERE `is_cat` = 0 ORDER BY `forumid` ASC" );
929
+ if( ! empty( $forumids ) ) {
930
+ foreach( $forumids as $forumid ) {
931
+ WPF()->forum->rebuild_stats( $forumid );
932
+ }
933
+ WPF()->statistic_cache_clean();
934
+ WPF()->forum->delete_tree_cache();
935
+ WPF()->notice->add( 'Updated Successfully!', 'success' );
936
+ }
937
+
938
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) );
939
+ exit();
940
+ }
941
+
942
+ /**
943
+ * rebuild topics statistics first|last posts etc.
944
+ */
945
+ public function reset_topics_stats() {
946
+ check_admin_referer( 'wpforo_reset_topics_stat' );
947
+
948
+ if( ! current_user_can( 'administrator' ) ) {
949
+ WPF()->notice->add( 'Permission denied', 'error' );
950
+ wp_safe_redirect( admin_url() );
951
+ exit();
952
+ }
953
+
954
+ if( $this->can_do() ) {
955
+ wpforo_set_max_execution_time();
956
+ wp_raise_memory_limit();
957
+
958
+ $lastid = (int) wpfval( $_GET, 'topic_lastid' );
959
+ $sql = "SELECT `topicid` FROM " . WPF()->tables->topics . " WHERE `topicid` > %d ORDER BY `topicid` ASC LIMIT %d";
960
+ $topicids = WPF()->db->get_col( WPF()->db->prepare( $sql, $lastid, apply_filters( 'wpforo_rebuild_per_request', 200 ) ) );
961
+ if( $topicids ) {
962
+ foreach( $topicids as $topicid ) {
963
+ $topic = WPF()->topic->get_topic( $topicid );
964
+ WPF()->topic->rebuild_first_last( $topic );
965
+ WPF()->topic->rebuild_stats( $topic );
966
+ }
967
+ wp_safe_redirect( htmlspecialchars_decode( wp_nonce_url( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) . '&wpfaction=reset_topics_stats&topic_lastid=' . end( $topicids ) ), 'wpforo_reset_topics_stat' ) ) );
968
+ } else {
969
+ @WPF()->db->query(
970
+ "UPDATE `" . WPF()->tables->topics . "` t
971
+ INNER JOIN `" . WPF()->tables->posts . "` p ON p.`topicid` = t.`topicid` AND p.`is_answer` = 1
972
+ SET t.`solved` = 1
973
+ WHERE t.`solved` = 0"
974
+ );
975
+ WPF()->statistic_cache_clean();
976
+ WPF()->notice->add( 'Updated Successfully!', 'success' );
977
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) );
978
+ }
979
+ exit();
980
+ }
981
+ }
982
+
983
+ /**
984
+ * rebuild users statistics etc.
985
+ */
986
+ public function reset_users_stats() {
987
+ check_admin_referer( 'wpforo_reset_users_stat' );
988
+
989
+ if( ! current_user_can( 'administrator' ) ) {
990
+ WPF()->notice->add( 'Permission denied', 'error' );
991
+ wp_safe_redirect( admin_url() );
992
+ exit();
993
+ }
994
+
995
+ if( $this->can_do() ) {
996
+ wpforo_set_max_execution_time();
997
+ wp_raise_memory_limit();
998
+
999
+ $lastid = (int) wpfval( $_GET, 'user_lastid' );
1000
+ $sql = "SELECT `userid` FROM " . WPF()->tables->profiles . " WHERE `userid` > %d ORDER BY `userid` ASC LIMIT %d";
1001
+ $userids = WPF()->db->get_col( WPF()->db->prepare( $sql, $lastid, apply_filters( 'wpforo_rebuild_per_request', 200 ) ) );
1002
+ if( $userids ) {
1003
+ foreach( $userids as $userid ) {
1004
+ WPF()->member->rebuild_stats( $userid );
1005
+ }
1006
+
1007
+ wp_safe_redirect( htmlspecialchars_decode( wp_nonce_url( admin_url( 'admin.php?page=wpforo-overview&wpfaction=reset_users_stats&user_lastid=' . end( $userids ) ), 'wpforo_reset_users_stat' ) ) );
1008
+ } else {
1009
+ WPF()->notice->add( 'Updated Successfully!', 'success' );
1010
+ wp_safe_redirect( admin_url( 'admin.php?page=wpforo-overview' ) );
1011
+ }
1012
+ exit();
1013
+ }
1014
+ }
1015
+
1016
+ /**
1017
+ * rebuild 4 layout forum topics threads root
1018
+ */
1019
+ public function rebuild_threads() {
1020
+ check_admin_referer( 'wpforo_rebuild_threads' );
1021
+
1022
+ if( ! current_user_can( 'administrator' ) ) {
1023
+ WPF()->notice->add( 'Permission denied', 'error' );
1024
+ wp_safe_redirect( admin_url() );
1025
+ exit();
1026
+ }
1027
+
1028
+ wpforo_set_max_execution_time( 3600 );
1029
+ wp_raise_memory_limit();
1030
+
1031
+ WPF()->topic->rebuild_forum_threads();
1032
+ wpforo_clean_cache();
1033
+ WPF()->notice->add( 'Threads rebuilt successfully', 'success' );
1034
+
1035
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) );
1036
+ exit();
1037
+ }
1038
+
1039
+ /**
1040
+ * reset phrases cache from db
1041
+ */
1042
+ public function reset_phrase_cache() {
1043
+ check_admin_referer( 'wpforo_reset_phrase_cache' );
1044
+
1045
+ if( ! current_user_can( 'administrator' ) ) {
1046
+ WPF()->notice->add( 'Permission denied', 'error' );
1047
+ wp_safe_redirect( admin_url() );
1048
+ exit();
1049
+ }
1050
+
1051
+ wpforo_set_max_execution_time();
1052
+ wp_raise_memory_limit();
1053
+
1054
+ WPF()->phrase->clear_cache();
1055
+ WPF()->notice->add( 'Deleted Successfully!', 'success' );
1056
+
1057
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) );
1058
+ exit();
1059
+ }
1060
+
1061
+ /**
1062
+ * recrawling phrases from all wpforo, wpforo-addons code files
1063
+ */
1064
+ public function recrawl_phrases() {
1065
+ check_admin_referer( 'wpforo_recrawl_phrases' );
1066
+
1067
+ if( ! current_user_can( 'administrator' ) ) {
1068
+ WPF()->notice->add( 'Permission denied', 'error' );
1069
+ wp_safe_redirect( admin_url() );
1070
+ exit();
1071
+ }
1072
+
1073
+ wpforo_set_max_execution_time();
1074
+ wp_raise_memory_limit();
1075
+
1076
+ WPF()->phrase->crawl_phrases();
1077
+ WPF()->phrase->clear_cache();
1078
+ WPF()->notice->clear();
1079
+ WPF()->notice->add( 'Rebuilt Successfully!', 'success' );
1080
+
1081
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) );
1082
+ exit();
1083
+ }
1084
+
1085
+ /**
1086
+ * reset wpforo all caches (phrase, user, forum, post, stats) etc.
1087
+ */
1088
+ public function reset_all_caches() {
1089
+ check_admin_referer( 'wpforo_reset_cache' );
1090
+
1091
+ if( ! current_user_can( 'administrator' ) ) {
1092
+ WPF()->notice->add( 'Permission denied', 'error' );
1093
+ wp_safe_redirect( admin_url() );
1094
+ exit();
1095
+ }
1096
+
1097
+ wpforo_set_max_execution_time();
1098
+ wp_raise_memory_limit();
1099
+
1100
+ WPF()->member->clear_db_cache();
1101
+ wpforo_clean_cache();
1102
+ WPF()->notice->add( 'Deleted Successfully!', 'success' );
1103
+
1104
+ $redirect = ( is_admin() ) ? admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) : wpforo_home_url();
1105
+ wp_safe_redirect( $redirect );
1106
+ exit();
1107
+ }
1108
+
1109
+ /**
1110
+ * Clean Up damaged content in database
1111
+ */
1112
+ public function clean_up() {
1113
+ check_admin_referer( 'wpforo_clean_up' );
1114
+
1115
+ if( ! current_user_can( 'administrator' ) ) {
1116
+ WPF()->notice->add( 'Permission denied', 'error' );
1117
+ wp_safe_redirect( admin_url() );
1118
+ exit();
1119
+ }
1120
+
1121
+ wpforo_set_max_execution_time();
1122
+ wp_raise_memory_limit();
1123
+
1124
+ wpforo_clean_up();
1125
+ WPF()->notice->add( 'Cleaned Up!', 'success' );
1126
+
1127
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) );
1128
+ exit();
1129
+ }
1130
+
1131
+ /**
1132
+ * dashboard_options_save form submit action
1133
+ */
1134
+ public function dashboard_options_save() {
1135
+ if( ! current_user_can( 'administrator' ) ) {
1136
+ WPF()->notice->add( 'Permission denied', 'error' );
1137
+ wp_safe_redirect( admin_url() );
1138
+ exit();
1139
+ }
1140
+ if( $dashboard_count_per_page = (int) wpfval( $_POST, 'wpforo_dashboard_count_per_page' ) ) {
1141
+ wpforo_update_option( 'count_per_page', $dashboard_count_per_page );
1142
+ }
1143
+ wp_safe_redirect( wpforo_get_request_uri() );
1144
+ exit();
1145
+ }
1146
+
1147
+ /**
1148
+ * checking accesses to forum admin menu pages settings etc...
1149
+ */
1150
+ public function check_dashboard_permissions() {
1151
+ $page = wpfval( WPF()->GET, 'page' );
1152
+ if( $page === wpforo_prefix_slug( 'settings' ) ) {
1153
+ if( ! WPF()->usergroup->can( 'ms' ) ) {
1154
+ WPF()->notice->add( 'Permission denied', 'error' );
1155
+ wp_safe_redirect( admin_url() );
1156
+ exit();
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
+ /**
1162
+ * check if [wpforo] page has been deleted, restore or create new [wpforo] page
1163
+ */
1164
+ public function repair_lost_main_shortcode_page() {
1165
+ if( wpfval( WPF()->GET, 'page' ) === wpforo_prefix_slug( 'settings' ) ) wpforo_repair_main_shortcode_page();
1166
+ }
1167
+
1168
+ /**
1169
+ * add_new_xml_translation form submit action
1170
+ */
1171
+ public function add_new_xml_translation() {
1172
+ check_admin_referer( 'wpforo-settings-language' );
1173
+
1174
+ if( ! WPF()->usergroup->can( 'ms' ) ) {
1175
+ WPF()->notice->add( 'Permission denied', 'error' );
1176
+ wp_safe_redirect( admin_url() );
1177
+ exit();
1178
+ }
1179
+
1180
+ if( ! empty( $_FILES['add_lang'] ) ) {
1181
+ WPF()->phrase->add_lang();
1182
+ wpforo_clean_cache();
1183
+ }
1184
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) . '&tab=general' ) );
1185
+ exit();
1186
+ }
1187
+
1188
+ /**
1189
+ * add_new_xml_translation form submit action
1190
+ */
1191
+ public function phrases_change_lang() {
1192
+ check_admin_referer( 'wpforo-phrases-change-language' );
1193
+
1194
+ if( $langid = (int) wpfval( $_POST, 'langid' ) ){
1195
+ if( WPF()->phrase->set_language_status( $langid ) ){
1196
+ WPF()->notice->add( 'Successfully updated', 'success' );
1197
+ }else{
1198
+ WPF()->notice->add( 'Invalid request!', 'error' );
1199
+ }
1200
+ }
1201
+
1202
+ wp_safe_redirect( wp_get_raw_referer() );
1203
+ exit();
1204
+ }
1205
+
1206
+ /**
1207
+ * colors.css download action
1208
+ */
1209
+ public function colors_css_download() {
1210
+ check_admin_referer( 'dynamic_css_download' );
1211
+
1212
+ if( ! WPF()->usergroup->can( 'ms' ) ) {
1213
+ WPF()->notice->add( 'Permission denied', 'error' );
1214
+ wp_safe_redirect( admin_url() );
1215
+ exit();
1216
+ }
1217
+
1218
+ $dynamic_css = WPF()->tpl->generate_dynamic_css();
1219
+ header( 'Content-Type: application/download' );
1220
+ header( 'Content-Disposition: attachment; filename="colors.css"' );
1221
+ header( 'Content-Transfer-Encoding: binary' );
1222
+ header( "Content-Length: " . strlen( $dynamic_css ) );
1223
+ echo $dynamic_css;
1224
+ exit();
1225
+ }
1226
+
1227
+
1228
+ /**
1229
+ * cleanup_options_save form submit action
1230
+ */
1231
+ public function cleanup_options_save() {
1232
+ check_admin_referer( 'wpforo-tools-cleanup' );
1233
+
1234
+ if( ! WPF()->usergroup->can( 'mt' ) ) {
1235
+ WPF()->notice->add( 'Permission denied', 'error' );
1236
+ wp_safe_redirect( admin_url() );
1237
+ exit();
1238
+ }
1239
+
1240
+ if( ! wpfkey( $_POST, 'reset' ) ) {
1241
+ if( $options = wpfval( $_POST, 'wpforo_tools_cleanup' ) ) {
1242
+ if( wpforo_update_option( 'tools_cleanup', $options ) ) {
1243
+ WPF()->notice->add( 'Settings successfully updated', 'success' );
1244
+ }
1245
+ }
1246
+ } else {
1247
+ wpforo_delete_option( 'tools_cleanup' );
1248
+ WPF()->notice->add( 'Cleanup options reset successfully', 'success' );
1249
+ }
1250
+
1251
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=cleanup' ) );
1252
+ exit();
1253
+ }
1254
+
1255
+ /**
1256
+ * misc_options_save form submit action
1257
+ */
1258
+ public function misc_options_save() {
1259
+ check_admin_referer( 'wpforo-tools-misc' );
1260
+
1261
+ if( ! WPF()->usergroup->can( 'mt' ) ) {
1262
+ WPF()->notice->add( 'Permission denied', 'error' );
1263
+ wp_safe_redirect( admin_url() );
1264
+ exit();
1265
+ }
1266
+
1267
+ if( ! wpfkey( $_POST, 'reset' ) ) {
1268
+ if( $options = wpfval( $_POST, 'wpforo_tools_misc' ) ) {
1269
+ $options['admin_note'] = wpforo_kses( $options['admin_note'] );
1270
+ $options['admin_note_groups'] = ( wpfval( $_POST, 'wpforo_tools_misc', 'admin_note_groups' ) ) ? array_map( 'intval', $options['admin_note_groups'] ) : [];
1271
+ $options['admin_note_pages'] = ( wpfval( $_POST, 'wpforo_tools_misc', 'admin_note_pages' ) ) ? array_map( 'sanitize_textarea_field', $options['admin_note_pages'] ) : [];
1272
+ if( wpforo_update_option( 'tools_misc', $options ) ) {
1273
+ wpforo_clean_cache( 'forum-soft' );
1274
+ WPF()->notice->add( 'Settings successfully updated', 'success' );
1275
+ }
1276
+ }
1277
+ } else {
1278
+ wpforo_delete_option( 'tools_misc' );
1279
+ WPF()->notice->add( 'Misc options reset successfully', 'success' );
1280
+ }
1281
+
1282
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=misc' ) );
1283
+ exit();
1284
+ }
1285
+
1286
+ /**
1287
+ * legal_options_save form submit action
1288
+ */
1289
+ public function legal_options_save() {
1290
+ check_admin_referer( 'wpforo-tools-legal' );
1291
+
1292
+ if( ! WPF()->usergroup->can( 'mt' ) ) {
1293
+ WPF()->notice->add( 'Permission denied', 'error' );
1294
+ wp_safe_redirect( admin_url() );
1295
+ exit();
1296
+ }
1297
+
1298
+ if( ! wpfkey( $_POST, 'reset' ) ) {
1299
+ if( $options = wpfval( $_POST, 'wpforo_tools_legal' ) ) {
1300
+ $options['contact_page_url'] = esc_url( $options['contact_page_url'] );
1301
+ $options['checkbox_terms_privacy'] = intval( $options['checkbox_terms_privacy'] );
1302
+ $options['checkbox_email_password'] = intval( $options['checkbox_email_password'] );
1303
+ $options['page_terms'] = esc_url( $options['page_terms'] );
1304
+ $options['page_privacy'] = esc_url( $options['page_privacy'] );
1305
+ $options['checkbox_forum_privacy'] = intval( $options['checkbox_forum_privacy'] );
1306
+ $options['forum_privacy_text'] = wpforo_kses( $options['forum_privacy_text'], 'post' );
1307
+ $options['checkbox_fb_login'] = intval( $options['checkbox_fb_login'] );
1308
+ $options['cookies'] = intval( $options['cookies'] );
1309
+ $options['rules_checkbox'] = intval( $options['rules_checkbox'] );
1310
+ $options['rules_text'] = wpforo_kses( $options['rules_text'], 'post' );
1311
+ if( wpforo_update_option( 'tools_legal', $options ) ) {
1312
+ WPF()->notice->add( 'Settings successfully updated', 'success' );
1313
+ }
1314
+ }
1315
+ } else {
1316
+ wpforo_delete_option( 'tools_legal' );
1317
+ WPF()->notice->add( 'Settings reset successfully', 'success' );
1318
+ }
1319
+
1320
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=legal' ) );
1321
+ exit();
1322
+ }
1323
+
1324
+ /**
1325
+ * delete detected spam file
1326
+ */
1327
+ public function delete_spam_file() {
1328
+ check_admin_referer( 'wpforo_tools_antispam_files' );
1329
+
1330
+ if( ! WPF()->usergroup->can( 'mt' ) ) {
1331
+ WPF()->notice->add( 'Permission denied', 'error' );
1332
+ wp_safe_redirect( admin_url() );
1333
+ exit();
1334
+ }
1335
+
1336
+ if( $filename = trim( wpfval( $_GET, 'sfname' ) ) ) {
1337
+ $filename = str_replace( [ '../', './', '/' ], '', sanitize_file_name( $filename ) );
1338
+ $filename = urldecode( $filename );
1339
+ if( $filename ) {
1340
+ $attachmentid = WPF()->post->get_attachment_id( '/' . $filename );
1341
+ if( ! wp_delete_attachment( $attachmentid ) ) {
1342
+ @unlink( WPF()->folders['default_attachments']['dir'] . DIRECTORY_SEPARATOR . $filename );
1343
+ }
1344
+ WPF()->notice->add( 'Deleted', 'success' );
1345
+ }
1346
+ }
1347
+
1348
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam' ) );
1349
+ exit();
1350
+ }
1351
+
1352
+ /**
1353
+ * delete_all_spam_files all detected spam file using level attribute
1354
+ */
1355
+ public function delete_all_spam_files() {
1356
+ check_admin_referer( 'wpforo_tools_antispam_files' );
1357
+
1358
+ if( ! WPF()->usergroup->can( 'mt' ) ) {
1359
+ WPF()->notice->add( 'Permission denied', 'error' );
1360
+ wp_safe_redirect( admin_url() );
1361
+ exit();
1362
+ }
1363
+
1364
+ if( $delete_level = (int) wpfval( $_GET, 'level' ) ) {
1365
+ $default_attachments_dir = WPF()->folders['default_attachments']['dir'];
1366
+ if( is_dir( $default_attachments_dir ) ) {
1367
+ if( $handle = opendir( $default_attachments_dir ) ) {
1368
+ while( false !== ( $filename = readdir( $handle ) ) ) {
1369
+ if( $filename === '.' || $filename === '..' ) continue;
1370
+ if( ! $level = WPF()->moderation->spam_file( $filename ) ) continue;
1371
+ if( $delete_level === $level ) {
1372
+ $attachmentid = WPF()->post->get_attachment_id( '/' . $filename );
1373
+ if( ! wp_delete_attachment( $attachmentid ) ) {
1374
+ @unlink( $default_attachments_dir . DIRECTORY_SEPARATOR . $filename );
1375
+ }
1376
+ }
1377
+ }
1378
+ closedir( $handle );
1379
+ WPF()->notice->add( 'Deleted', 'success' );
1380
+ }
1381
+ }
1382
+ }
1383
+
1384
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=antispam' ) );
1385
+ exit();
1386
+ }
1387
+
1388
+ /**
1389
+ * do database alter fixing using install.sql db-strukture
1390
+ */
1391
+ public function database_update() {
1392
+ check_admin_referer( 'wpforo_update_database' );
1393
+
1394
+ if( ! WPF()->usergroup->can( 'mt' ) ) {
1395
+ WPF()->notice->add( 'Permission denied', 'error' );
1396
+ wp_safe_redirect( admin_url() );
1397
+ exit();
1398
+ }
1399
+
1400
+ wpforo_set_max_execution_time( 3600 );
1401
+
1402
+ wpforo_update_db();
1403
+
1404
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=debug&view=tables' ) );
1405
+ exit();
1406
+ }
1407
+
1408
+ /**
1409
+ * forum_add form submit action
1410
+ */
1411
+ public function forum_add() {
1412
+ check_admin_referer( 'wpforo-forum-add' );
1413
+
1414
+ if( ! WPF()->usergroup->can_manage_forum() ) {
1415
+ WPF()->notice->add( 'Permission denied', 'error' );
1416
+ wp_safe_redirect( admin_url() );
1417
+ exit();
1418
+ }
1419
+
1420
+ if( ! empty( $_REQUEST['forum'] ) ) {
1421
+ WPF()->forum->add();
1422
+ }
1423
+
1424
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) ) );
1425
+ exit();
1426
+ }
1427
+
1428
+ /**
1429
+ * forum_edit form submit action
1430
+ */
1431
+ public function forum_edit() {
1432
+ check_admin_referer( 'wpforo-forum-edit' );
1433
+
1434
+ if( ! WPF()->usergroup->can_manage_forum() ) {
1435
+ WPF()->notice->add( 'Permission denied', 'error' );
1436
+ wp_safe_redirect( admin_url() );
1437
+ exit();
1438
+ }
1439
+
1440
+ if( ! empty( $_REQUEST['forum'] ) ) {
1441
+ WPF()->forum->edit();
1442
+ }
1443
+
1444
+ wp_safe_redirect( wpforo_get_request_uri() );
1445
+ exit();
1446
+ }
1447
+
1448
+ /**
1449
+ * forum_delete form submit action
1450
+ */
1451
+ public function forum_delete() {
1452
+ check_admin_referer( 'wpforo-forum-delete' );
1453
+
1454
+ if( ! WPF()->usergroup->can_manage_forum() ) {
1455
+ WPF()->notice->add( 'Permission denied', 'error' );
1456
+ wp_safe_redirect( admin_url() );
1457
+ exit();
1458
+ }
1459
+
1460
+ $delete = (int) wpfval( $_REQUEST, 'forum', 'delete' );
1461
+ if( $delete === 1 ) {
1462
+ WPF()->forum->delete( 0, false );
1463
+ } elseif( $delete === 0 ) {
1464
+ WPF()->forum->merge();
1465
+ }
1466
+
1467
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) ) );
1468
+ exit();
1469
+ }
1470
+
1471
+ /**
1472
+ * forum_hierarchy_save form submit action
1473
+ */
1474
+ public function forum_hierarchy_save() {
1475
+ check_admin_referer( 'wpforo-forums-hierarchy' );
1476
+
1477
+ if( ! WPF()->usergroup->can_manage_forum() ) {
1478
+ WPF()->notice->add( 'Permission denied', 'error' );
1479
+ wp_safe_redirect( admin_url() );
1480
+ exit();
1481
+ }
1482
+
1483
+ if( ! empty( $_REQUEST['forum'] ) ) {
1484
+ WPF()->forum->update_hierarchy();
1485
+ wpforo_clean_cache( 'forum' );
1486
+ }
1487
+
1488
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) ) );
1489
+ exit();
1490
+ }
1491
+
1492
+ /**
1493
+ * dashboard_post_unapprove action
1494
+ */
1495
+ public function dashboard_post_unapprove() {
1496
+ $postid = wpfval( $_GET, 'postid' );
1497
+ check_admin_referer( "wpforo-unapprove-post-{$postid}" );
1498
+
1499
+ if( ! WPF()->usergroup->can( 'aum' ) ) {
1500
+ WPF()->notice->add( 'Permission denied', 'error' );
1501
+ wp_safe_redirect( admin_url() );
1502
+ exit();
1503
+ }
1504
+
1505
+ WPF()->moderation->post_unapprove( $postid );
1506
+ wpforo_clean_cache( 'post', $postid );
1507
+
1508
+ wp_safe_redirect( wp_get_referer() );
1509
+ exit();
1510
+ }
1511
+
1512
+ /**
1513
+ * dashboard_post_approve action
1514
+ */
1515
+ public function dashboard_post_approve() {
1516
+ $postid = wpfval( $_GET, 'postid' );
1517
+ check_admin_referer( "wpforo-approve-post-{$postid}" );
1518
+
1519
+ if( ! WPF()->usergroup->can( 'aum' ) ) {
1520
+ WPF()->notice->add( 'Permission denied', 'error' );
1521
+ wp_safe_redirect( admin_url() );
1522
+ exit();
1523
+ }
1524
+
1525
+ WPF()->moderation->post_approve( $postid );
1526
+ wpforo_clean_cache( 'post', $postid );
1527
+
1528
+ wp_safe_redirect( wp_get_referer() );
1529
+ exit();
1530
+ }
1531
+
1532
+ /**
1533
+ * dashboard_post_delete action
1534
+ */
1535
+ public function dashboard_post_delete() {
1536
+ $postid = wpfval( $_GET, 'postid' );
1537
+ check_admin_referer( "wpforo-delete-post-{$postid}" );
1538
+
1539
+ if( ! WPF()->usergroup->can( 'aum' ) ) {
1540
+ WPF()->notice->add( 'Permission denied', 'error' );
1541
+ wp_safe_redirect( admin_url() );
1542
+ exit();
1543
+ }
1544
+
1545
+ WPF()->post->delete( $postid );
1546
+
1547
+ wp_safe_redirect( wp_get_referer() );
1548
+ exit();
1549
+ }
1550
+
1551
+ /**
1552
+ * doing bulk moderation actions ( approve, unapprove, delete )
1553
+ */
1554
+ public function bulk_moderation() {
1555
+ check_admin_referer( 'bulk-moderations' );
1556
+
1557
+ if( ! WPF()->usergroup->can( 'aum' ) ) {
1558
+ WPF()->notice->add( 'Permission denied', 'error' );
1559
+ wp_safe_redirect( admin_url() );
1560
+ exit();
1561
+ }
1562
+
1563
+ $u_action = $this->get_current_bulk_action();
1564
+ $postids = (array) wpfval( $_GET, 'postids' );
1565
+ if( $u_action && ! empty( $postids ) ) {
1566
+ if( $u_action === 'delete' ) {
1567
+ foreach( $postids as $postid ) WPF()->post->delete( $postid );
1568
+ } elseif( $u_action === 'approve' ) {
1569
+ foreach( $postids as $postid ) WPF()->moderation->post_approve( $postid );
1570
+ } elseif( $u_action === 'unapprove' ) {
1571
+ foreach( $postids as $postid ) WPF()->moderation->post_unapprove( $postid );
1572
+ }
1573
+ }
1574
+
1575
+ wp_safe_redirect( wp_get_referer() );
1576
+ exit();
1577
+ }
1578
+
1579
+ /**
1580
+ * phrase_add form submit action
1581
+ */
1582
+ public function phrase_add() {
1583
+ check_admin_referer( 'wpforo-phrase-add' );
1584
+
1585
+ if( ! WPF()->usergroup->can( 'mp' ) ) {
1586
+ WPF()->notice->add( 'Permission denied', 'error' );
1587
+ wp_safe_redirect( admin_url() );
1588
+ exit();
1589
+ }
1590
+
1591
+ if( ! empty( $_POST['phrase'] ) ) {
1592
+ WPF()->phrase->add();
1593
+ }
1594
+
1595
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) ) );
1596
+ exit();
1597
+ }
1598
+
1599
+ /**
1600
+ * phrase_edit_form action redirect to phrase list page when phraseid(s) not chosen
1601
+ */
1602
+ public function phrase_edit_form() {
1603
+ $phraseids = array_filter( array_map( 'intval', array_merge( (array) wpfval( $_GET, 'phraseid' ), (array) wpfval( $_GET, 'phraseids' ) ) ) );
1604
+ if( ! $phraseids ) {
1605
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) ) );
1606
+ exit();
1607
+ }
1608
+ }
1609
+
1610
+ /**
1611
+ * phrase_edit form submit action
1612
+ */
1613
+ public function phrase_edit() {
1614
+ check_admin_referer( 'wpforo-phrases-edit' );
1615
+
1616
+ if( ! WPF()->usergroup->can( 'mp' ) ) {
1617
+ WPF()->notice->add( 'Permission denied', 'error' );
1618
+ wp_safe_redirect( admin_url() );
1619
+ exit();
1620
+ }
1621
+
1622
+ if( ! empty( $_POST['phrases'] ) ) {
1623
+ WPF()->phrase->edit();
1624
+ }
1625
+
1626
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'phrases' ) ) );
1627
+ exit();
1628
+ }
1629
+
1630
+ /**
1631
+ * user_ban action
1632
+ */
1633
+ public function user_ban() {
1634
+ $userid = intval( wpfval( $_GET, 'userid' ) );
1635
+ check_admin_referer( 'wpforo-user-ban-' . $userid );
1636
+
1637
+ if( ! WPF()->usergroup->can( 'vm' ) || ! WPF()->usergroup->can( 'bm' ) || $userid === WPF()->current_userid ) {
1638
+ WPF()->notice->add( 'Permission denied', 'error' );
1639
+ wp_safe_redirect( admin_url() );
1640
+ exit();
1641
+ }
1642
+
1643
+ WPF()->member->ban( $userid );
1644
+ wpforo_clean_cache( 'user' );
1645
+
1646
+ wp_safe_redirect( wp_get_referer() );
1647
+ exit();
1648
+ }
1649
+
1650
+ /**
1651
+ * user_unban action
1652
+ */
1653
+ public function user_unban() {
1654
+ $userid = intval( wpfval( $_GET, 'userid' ) );
1655
+ check_admin_referer( 'wpforo-user-unban-' . $userid );
1656
+
1657
+ if( ! WPF()->usergroup->can( 'vm' ) || ! WPF()->usergroup->can( 'bm' ) || $userid === WPF()->current_userid ) {
1658
+ WPF()->notice->add( 'Permission denied', 'error' );
1659
+ wp_safe_redirect( admin_url() );
1660
+ exit();
1661
+ }
1662
+
1663
+ WPF()->member->unban( $userid );
1664
+ wpforo_clean_cache( 'user' );
1665
+
1666
+ wp_safe_redirect( wp_get_referer() );
1667
+ exit();
1668
+ }
1669
+
1670
+ /**
1671
+ * user_activate action
1672
+ */
1673
+ public function user_activate() {
1674
+ $userid = intval( wpfval( $_GET, 'userid' ) );
1675
+ check_admin_referer( 'wpforo-user-activate-' . $userid );
1676
+
1677
+ if( ! WPF()->usergroup->can( 'vm' ) || ! WPF()->usergroup->can( 'bm' ) || $userid === WPF()->current_userid ) {
1678
+ WPF()->notice->add( 'Permission denied', 'error' );
1679
+ wp_safe_redirect( admin_url() );
1680
+ exit();
1681
+ }
1682
+
1683
+ WPF()->member->activate( $userid );
1684
+
1685
+ wp_safe_redirect( wp_get_referer() );
1686
+ exit();
1687
+ }
1688
+
1689
+ /**
1690
+ * user_deactivate action
1691
+ */
1692
+ public function user_deactivate() {
1693
+ $userid = intval( wpfval( $_GET, 'userid' ) );
1694
+ check_admin_referer( 'wpforo-user-deactivate-' . $userid );
1695
+
1696
+ if( ! WPF()->usergroup->can( 'vm' ) || ! WPF()->usergroup->can( 'bm' ) || $userid === WPF()->current_userid ) {
1697
+ WPF()->notice->add( 'Permission denied', 'error' );
1698
+ wp_safe_redirect( admin_url() );
1699
+ exit();
1700
+ }
1701
+
1702
+ WPF()->member->deactivate( $userid );
1703
+
1704
+ wp_safe_redirect( wp_get_referer() );
1705
+ exit();
1706
+ }
1707
+
1708
+ public function user_ban_ajax() {
1709
+ $userid = WPF()->current_object['user']['userid'];
1710
+ $currentstate = (int) wpfval( $_POST, 'currentstate' );
1711
+ if( $currentstate ){
1712
+ $r = WPF()->member->unban( $userid );
1713
+ }else{
1714
+ $r = WPF()->member->ban( $userid );
1715
+ }
1716
+
1717
+ if( $r ){
1718
+ wp_send_json_success( [
1719
+ 'currentstate' => (int) !$currentstate,
1720
+ 'notice' => WPF()->notice->get_notices(),
1721
+ ] );
1722
+ }else{
1723
+ wp_send_json_error( [ 'notice' => WPF()->notice->get_notices() ] );
1724
+ }
1725
+ }
1726
+
1727
+ public function get_member_template() {
1728
+ $href = wpfval( $_POST, 'href' );
1729
+ WPF()->init_current_url( $href );
1730
+ WPF()->init_current_object();
1731
+ if( wpforo_is_member_template() ){
1732
+ if( ( $template = WPF()->tpl->get_template( WPF()->current_object['template'] ) ) && $template['type'] === 'callback' && is_callable( $template['callback_for_page'] ) ){
1733
+ ob_start();
1734
+ echo call_user_func( $template['callback_for_page'], $template );
1735
+ wp_send_json_success( [ 'html' => ob_get_clean() ] );
1736
+ }
1737
+ }
1738
+ wp_send_json_error();
1739
+ }
1740
+
1741
+ public function search_existed_topics() {
1742
+ $title = trim( wpfval( $_POST, 'title' ) );
1743
+ $topicids = WPF()->topic->search( $title, 'title' );
1744
+ if( $topicids ){
1745
+ $topics = WPF()->topic->get_topics( [ 'include' => $topicids, 'row_count' => 10, 'orderby' => 'created' ] );
1746
+ if( $topics ){
1747
+ $topics = array_map( function( $topic ){
1748
+ $topic['url'] = WPF()->topic->get_topic_url( $topic );
1749
+ return $topic;
1750
+ }, $topics );
1751
+
1752
+ wp_send_json_success( $topics );
1753
+ }
1754
+ }
1755
+
1756
+ wp_send_json_error();
1757
+ }
1758
+
1759
+ public function user_delete() {
1760
+ if(
1761
+ WPF()->current_object['user']
1762
+ && ( $action = WPF()->member->get_action( WPF()->current_object['user'], 'delete' ) )
1763
+ && is_callable( $action['callback_for_can'] )
1764
+ && call_user_func( $action['callback_for_can'] )
1765
+ ){
1766
+ if( ! function_exists( 'wp_delete_user' ) ) require_once ABSPATH . "wp-admin/includes/user.php";
1767
+ if( wp_delete_user( WPF()->current_object['user']['userid'] ) ){
1768
+ WPF()->notice->add( 'User successfully deleted', 'success' );
1769
+ }else{
1770
+ WPF()->notice->add( 'User delete error', 'error' );
1771
+ }
1772
+ }else{
1773
+ WPF()->notice->add( 'Permission denied for this action', 'error' );
1774
+ }
1775
+
1776
+ wp_safe_redirect( wpforo_url( '', 'members' ) );
1777
+ exit();
1778
+ }
1779
+
1780
+ /**
1781
+ * action after WordPress native deleted_user hook
1782
+ *
1783
+ * @param int $userid already deleted user ID
1784
+ */
1785
+ public function deleted_user( $userid, $reassign = null ) {
1786
+ if( wpfval( $_REQUEST, 'wpforo_user_delete_option' ) === 'reassign' ){
1787
+ WPF()->member->delete( $userid, wpforo_bigintval( wpfval( $_REQUEST, 'wpforo_reassign_userid' ) ) );
1788
+ }elseif( wpfval( $_REQUEST, 'wpforo_user_delete_option' ) === 'delete' ){
1789
+ WPF()->member->delete( $userid );
1790
+ }elseif( $reassign ){
1791
+ WPF()->member->delete( $userid, $reassign );
1792
+ }else{
1793
+ WPF()->member->delete( $userid, ( wpforo_setting( 'authorization', 'user_delete_method' ) === 'soft' ? 0 : null ) );
1794
+ }
1795
+ WPF()->notice->clear();
1796
+ }
1797
+
1798
+ /**
1799
+ * doing bulk member actions ( ban, unban, delete )
1800
+ */
1801
+ public function bulk_members() {
1802
+ check_admin_referer( 'bulk-members' );
1803
+
1804
+ if( ! WPF()->usergroup->can( 'vm' ) ) {
1805
+ WPF()->notice->add( 'Permission denied', 'error' );
1806
+ wp_safe_redirect( admin_url() );
1807
+ exit();
1808
+ }
1809
+
1810
+ $new_groupid = - 1;
1811
+ if( ! empty( $_GET['new_groupid'] ) && $_GET['new_groupid'] !== '-1' ) {
1812
+ $new_groupid = intval( $_GET['new_groupid'] );
1813
+ } elseif( ! empty( $_GET['new_groupid2'] ) && $_GET['new_groupid2'] !== '-1' ) {
1814
+ $new_groupid = intval( $_GET['new_groupid2'] );
1815
+ }
1816
+
1817
+ $u_action = $this->get_current_bulk_action();
1818
+ if( in_array( $u_action, [ 'ban', 'unban', 'activate', 'deactivate' ] ) && ! WPF()->usergroup->can( 'bm' ) ) {
1819
+ WPF()->notice->add( 'Permission denied', 'error' );
1820
+ wp_safe_redirect( admin_url() );
1821
+ exit();
1822
+ } elseif( $u_action === 'delete' && ! WPF()->usergroup->can( 'dm' ) ) {
1823
+ WPF()->notice->add( 'Permission denied', 'error' );
1824
+ wp_safe_redirect( admin_url() );
1825
+ exit();
1826
+ }
1827
+
1828
+ $userids = (array) wpfval( $_GET, 'userids' );
1829
+ $userids = array_filter( array_map( 'wpforo_bigintval', $userids ) );
1830
+ $userids = array_diff( $userids, (array) WPF()->current_userid );
1831
+ if( $u_action && ! empty( $userids ) ) {
1832
+ if( $u_action === 'delete' ) {
1833
+ $url = self_admin_url( 'users.php?action=delete&users[]=' . implode( '&users[]=', $userids ) );
1834
+ $url = str_replace( '&amp;', '&', wp_nonce_url( $url, 'bulk-users' ) );
1835
+ wp_safe_redirect( $url );
1836
+ exit();
1837
+ } elseif( $u_action === 'ban' ) {
1838
+ foreach( $userids as $userid ) {
1839
+ WPF()->member->ban( $userid );
1840
+ }
1841
+ } elseif( $u_action === 'unban' ) {
1842
+ foreach( $userids as $userid ) {
1843
+ WPF()->member->unban( $userid );
1844
+ }
1845
+ } elseif( $u_action === 'activate' ) {
1846
+ foreach( $userids as $userid ) {
1847
+ WPF()->member->activate( $userid );
1848
+ }
1849
+ } elseif( $u_action === 'deactivate' ) {
1850
+ foreach( $userids as $userid ) {
1851
+ WPF()->member->deactivate( $userid );
1852
+ }
1853
+ }
1854
+ } elseif( ! $u_action && wpfkey( $_GET, 'change_group' ) ) {
1855
+ if( ! empty( $userids ) && $new_groupid !== - 1 ) {
1856
+ $status = WPF()->usergroup->set_users_groupid( [ $new_groupid => $userids ] );
1857
+ if( $status['success'] ) WPF()->notice->add( 'Usergroup is successfully changed for selected users', 'success' );
1858
+ } else {
1859
+ WPF()->notice->add( 'Please select users and usergroup', 'error' );
1860
+ }
1861
+ }
1862
+ wpforo_clean_cache( 'user' );
1863
+
1864
+ wp_safe_redirect( wp_get_referer() );
1865
+ exit();
1866
+ }
1867
+
1868
+ /**
1869
+ * usergroup_add form submit action
1870
+ */
1871
+ public function usergroup_add() {
1872
+ check_admin_referer( 'wpforo-usergroup-add' );
1873
+
1874
+ if( ! WPF()->usergroup->can( 'vmg' ) ) {
1875
+ WPF()->notice->add( 'Permission denied', 'error' );
1876
+ wp_safe_redirect( admin_url() );
1877
+ exit();
1878
+ }
1879
+
1880
+ if( ! empty( $_POST['usergroup'] ) ) {
1881
+ $group = WPF()->usergroup->fix_group( $_POST['usergroup'] );
1882
+ $color = wpfval( $group, 'wpfugc' ) ? '' : sanitize_text_field( $group['color'] );
1883
+ $groupid = WPF()->usergroup->add( $group['name'], $group['cans'], $group['description'], $group['role'], $group['access'], $color, $group['visible'], $group['secondary'] );
1884
+ if( $groupid ) wpforo_clean_cache( 'loop', $groupid );
1885
+ }
1886
+
1887
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) ) );
1888
+ exit();
1889
+ }
1890
+
1891
+ /**
1892
+ * usergroup_edit form submit action
1893
+ */
1894
+ public function usergroup_edit() {
1895
+ check_admin_referer( 'wpforo-usergroup-edit' );
1896
+
1897
+ if( ! WPF()->usergroup->can( 'vmg' ) ) {
1898
+ WPF()->notice->add( 'Permission denied', 'error' );
1899
+ wp_safe_redirect( admin_url() );
1900
+ exit();
1901
+ }
1902
+
1903
+ if( ! empty( $_POST['usergroup'] ) ) {
1904
+ $group = WPF()->usergroup->fix_group( $_POST['usergroup'] );
1905
+ $color = wpfval( $group, 'wpfugc' ) ? '' : sanitize_text_field( $group['color'] );
1906
+ WPF()->usergroup->edit( $group['groupid'], $group['name'], $group['cans'], $group['description'], $group['role'], null, $color, $group['visible'], $group['secondary'] );
1907
+ wpforo_clean_cache( 'loop', $group['groupid'] );
1908
+ }
1909
+
1910
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) ) );
1911
+ exit();
1912
+ }
1913
+
1914
+ /**
1915
+ * usergroup_delete form submit action
1916
+ */
1917
+ public function usergroup_delete() {
1918
+ check_admin_referer( 'wpforo-usergroup-delete' );
1919
+
1920
+ if( ! WPF()->usergroup->can( 'vmg' ) ) {
1921
+ WPF()->notice->add( 'Permission denied', 'error' );
1922
+ wp_safe_redirect( admin_url() );
1923
+ exit();
1924
+ }
1925
+
1926
+ if( wpfval( $_POST, 'usergroup', 'delete' ) ) {
1927
+ $args = [ 'groupid' => wpfval( $_POST, 'usergroup', 'groupid' ) ];
1928
+ if( $userids = WPF()->member->get_userids( $args ) ) {
1929
+ $redirect_to = self_admin_url( 'users.php?action=delete&users[]=' . implode( '&users[]=', $userids ) );
1930
+ $redirect_to = str_replace( '&amp;', '&', wp_nonce_url( $redirect_to, 'bulk-users' ) );
1931
+ wp_safe_redirect( $redirect_to );
1932
+ exit();
1933
+ }
1934
+ }
1935
+
1936
+ if( ! empty( $_POST['usergroup'] ) ) {
1937
+ WPF()->usergroup->delete( wpfval( $_POST['usergroup'], 'groupid' ), wpfval( $_POST['usergroup'], 'mergeid' ) );
1938
+ wpforo_clean_cache( 'user' );
1939
+ }
1940
+
1941
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) ) );
1942
+ exit();
1943
+ }
1944
+
1945
+ /**
1946
+ * default_groupid_change action
1947
+ */
1948
+ public function default_groupid_change() {
1949
+ $default_groupid = intval( wpfval( $_GET, 'default_groupid' ) );
1950
+ check_admin_referer( 'wpforo-default-groupid-change-' . $default_groupid );
1951
+
1952
+ if( ! WPF()->usergroup->can( 'vmg' ) ) {
1953
+ WPF()->notice->add( 'Permission denied', 'error' );
1954
+ wp_safe_redirect( admin_url() );
1955
+ exit();
1956
+ }
1957
+
1958
+ if( $default_groupid ) WPF()->usergroup->set_default( $default_groupid );
1959
+
1960
+ wp_safe_redirect( wp_get_raw_referer() );
1961
+ exit();
1962
+ }
1963
+
1964
+ /**
1965
+ * prevent to show usergroup delete form when !$groupid || $groupid <= 5
1966
+ */
1967
+ public function usergroup_delete_form() {
1968
+ if( intval( wpfval( $_GET, 'groupid' ) ) <= 5 ) {
1969
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'usergroups' ) ) );
1970
+ exit();
1971
+ }
1972
+ }
1973
+
1974
+ /**
1975
+ * access_add form submit action
1976
+ */
1977
+ public function access_add() {
1978
+ check_admin_referer( 'wpforo-access-add' );
1979
+
1980
+ if( ! WPF()->usergroup->can( 'ms' ) ) {
1981
+ WPF()->notice->add( 'Permission denied', 'error' );
1982
+ wp_safe_redirect( admin_url() );
1983
+ exit();
1984
+ }
1985
+
1986
+ if( ! empty( $_POST['access'] ) ) {
1987
+ WPF()->perm->add( WPF()->perm->fix_access( $_POST['access'] ) );
1988
+ }
1989
+
1990
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'accesses' ) ) );
1991
+ exit();
1992
+ }
1993
+
1994
+ /**
1995
+ * access_edit form submit action
1996
+ */
1997
+ public function access_edit() {
1998
+ check_admin_referer( 'wpforo-access-edit' );
1999
+
2000
+ if( ! WPF()->usergroup->can( 'ms' ) ) {
2001
+ WPF()->notice->add( 'Permission denied', 'error' );
2002
+ wp_safe_redirect( admin_url() );
2003
+ exit();
2004
+ }
2005
+
2006
+ if( ! empty( $_POST['access'] ) ) {
2007
+ WPF()->perm->edit( WPF()->perm->fix_access( $_POST['access'] ) );
2008
+ wpforo_clean_cache( 'loop' );
2009
+ }
2010
+
2011
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'accesses' ) ) );
2012
+ exit();
2013
+ }
2014
+
2015
+ /**
2016
+ * access_delete form submit action
2017
+ */
2018
+ public function access_delete() {
2019
+ $accessid = intval( wpfval( $_GET, 'accessid' ) );
2020
+ check_admin_referer( 'wpforo-access-delete-' . $accessid );
2021
+
2022
+ if( ! WPF()->usergroup->can( 'ms' ) ) {
2023
+ WPF()->notice->add( 'Permission denied', 'error' );
2024
+ wp_safe_redirect( admin_url() );
2025
+ exit();
2026
+ }
2027
+
2028
+ WPF()->perm->delete( $accessid );
2029
+ wpforo_clean_cache( 'loop' );
2030
+
2031
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'accesses' ) ) );
2032
+ exit();
2033
+ }
2034
+
2035
+ public function theme_activate() {
2036
+ check_admin_referer( 'wpforo-theme-activate' );
2037
+
2038
+ if( ! WPF()->usergroup->can( 'mth' ) ) {
2039
+ WPF()->notice->add( 'Permission denied', 'error' );
2040
+ wp_safe_redirect( admin_url() );
2041
+ exit();
2042
+ }
2043
+
2044
+ $notice = __( 'Theme file not readable', 'wpforo' );
2045
+ $notice_type = 'error';
2046
+ if( ( $theme = trim( sanitize_text_field( wpfval( $_GET, 'theme' ) ) ) ) && WPF()->tpl->theme_exists( $theme ) ) {
2047
+ $general = WPF()->settings->general;
2048
+ $general['current_theme'] = $theme;
2049
+ wpforo_update_option( 'wpforo_general', $general );
2050
+ $notice = __( 'Theme Successfully Activated', 'wpforo' );
2051
+ $notice_type = 'success';
2052
+ }
2053
+
2054
+ WPF()->notice->add( $notice, $notice_type );
2055
+ wp_safe_redirect( wp_get_raw_referer() );
2056
+ exit();
2057
+ }
2058
+
2059
+ /**
2060
+ * theme_delete action
2061
+ */
2062
+ public function theme_delete() {
2063
+ check_admin_referer( 'wpforo-theme-delete' );
2064
+
2065
+ if( ! WPF()->usergroup->can( 'mth' ) ) {
2066
+ WPF()->notice->add( 'Permission denied', 'error' );
2067
+ wp_safe_redirect( admin_url() );
2068
+ exit();
2069
+ }
2070
+
2071
+ $notice = __( 'Theme delete error', 'wpforo' );
2072
+ $notice_type = 'error';
2073
+ if( $theme = trim( sanitize_text_field( wpfval( $_GET, 'theme' ) ) ) ) {
2074
+ if( WPF()->tpl->theme !== $theme ) {
2075
+ $remove_dir = WPFORO_THEME_DIR . '/' . $theme;
2076
+ if( is_dir( $remove_dir ) ) {
2077
+ wpforo_remove_directory( $remove_dir );
2078
+ $notice = __( 'Theme delete success', 'wpforo' );
2079
+ $notice_type = 'success';
2080
+ }
2081
+ }
2082
+ }
2083
+
2084
+ WPF()->notice->add( $notice, $notice_type );
2085
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'themes' ) ) );
2086
+ exit();
2087
+ }
2088
+
2089
+ /**
2090
+ * update wpForo addons CSS styles to make compatible with the current version of wpForo
2091
+ */
2092
+ function update_addons_css() {
2093
+ check_admin_referer( 'wpforo-update-addons-css' );
2094
+ wpforo_wrap_in_all_addons_css();
2095
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'settings' ) ) );
2096
+ exit();
2097
+ }
2098
+
2099
+ /**
2100
+ * dissmiss the poll version is old notification for admins
2101
+ */
2102
+ public function dissmiss_poll_version_is_old() {
2103
+ check_admin_referer( 'wpforo-dissmiss-poll-version-is-old' );
2104
+ WPF()->dissmissed['poll_version_is_old'] = 1;
2105
+ wpforo_update_option( 'dissmissed', WPF()->dissmissed );
2106
+ wp_safe_redirect( admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'dashboard' ) ) );
2107
+ exit();
2108
+ }
2109
+
2110
+ /**
2111
+ * dissmiss the recaptcha not configured notification for admins
2112
+ */
2113
+ public function dissmiss_recaptcha_note() {
2114
+ if( wpfval( $_POST, 'backend' ) ) {
2115
+ WPF()->dissmissed['recaptcha_backend_note'] = 1;
2116
+ } else {
2117
+ WPF()->dissmissed['recaptcha_note'] = 1;
2118
+ }
2119
+ $response = wpforo_update_option( 'dissmissed', WPF()->dissmissed );
2120
+ if( $response ) {
2121
+ wp_send_json_success();
2122
+ } else {
2123
+ wp_send_json_error();
2124
+ }
2125
+ }
2126
+
2127
+ /**
2128
+ * wpforo before deactivate action
2129
+ */
2130
+ public function deactivate() {
2131
+ $response = [ 'code' => 0 ];
2132
+ $json = filter_input( INPUT_POST, 'deactivateData' );
2133
+ if( $json ) {
2134
+ parse_str( $json, $data );
2135
+
2136
+ $blogTitle = get_option( 'blogname' );
2137
+ $to = 'feedback@wpforo.com';
2138
+ $subject = '[wpForo Feedback - ' . WPFORO_VERSION . ']';
2139
+ $headers = [];
2140
+ $contentType = 'text/html';
2141
+ $fromName = apply_filters( 'wp_mail_from_name', $blogTitle );
2142
+ $fromName = html_entity_decode( $fromName, ENT_QUOTES );
2143
+ $siteUrl = get_site_url();
2144
+ $parsedUrl = parse_url( $siteUrl );
2145
+ $domain = isset( $parsedUrl['host'] ) ? $parsedUrl['host'] : '';
2146
+ $fromEmail = 'no-reply@' . $domain;
2147
+ $headers[] = "Content-Type: $contentType; charset=UTF-8";
2148
+ $headers[] = "From: " . $fromName . " <" . $fromEmail . "> \r\n";
2149
+ $message = "Dismiss and never show again";
2150
+
2151
+ if( isset( $data['never_show'] ) && ( $v = intval( $data['never_show'] ) ) ) {
2152
+ wpforo_update_option( 'deactivation_dialog_never_show', $v );
2153
+ $response['code'] = 'dismiss_and_deactivate';
2154
+ } elseif( isset( $data['deactivation_reason'] ) && ( $reason = trim( $data['deactivation_reason'] ) ) ) {
2155
+ $subject .= ' - ' . $reason;
2156
+ $message = "<strong>Deactivation reason:</strong> " . $reason . "\r\n" . "<br/>";
2157
+ if( isset( $data['deactivation_reason_desc'] ) && ( $reasonDesc = trim( $data['deactivation_reason_desc'] ) ) ) {
2158
+ $message .= "<strong>Deactivation reason description:</strong> " . $reasonDesc . "\r\n" . "<br/>";
2159
+ }
2160
+ if( isset( $data['deactivation_feedback_email'] ) && ( $feedback_email = trim( $data['deactivation_feedback_email'] ) ) ) {
2161
+ $to = 'support@wpforo.com';
2162
+ $message .= "<strong>Feedback Email:</strong> " . $feedback_email . "\r\n" . "<br/>";
2163
+ }
2164
+ $subject = html_entity_decode( $subject, ENT_QUOTES );
2165
+ $message = html_entity_decode( $message, ENT_QUOTES );
2166
+ $response['code'] = 'send_and_deactivate';
2167
+ }
2168
+
2169
+ wp_mail( $to, $subject, $message, $headers );
2170
+ }
2171
+ wp_die( json_encode( $response ) );
2172
+ }
2173
+
2174
+ /**
2175
+ * base_slugs_settings_save from submit action
2176
+ *
2177
+ * @return void
2178
+ */
2179
+ public function base_slugs_settings_save() {
2180
+ check_admin_referer( 'wpforo_settings_save_general' );
2181
+
2182
+ if( wpfkey( $_POST, 'reset' ) ) {
2183
+ wpforo_delete_option( 'wpforo_base_slugs' );
2184
+ WPF()->notice->add( 'Successfully Done', 'success' );
2185
+ } else {
2186
+ $slugs = wpforo_array_args_cast_and_merge( array_filter( array_map( 'sanitize_title', wp_unslash( $_POST['slugs'] ) ) ), WPF()->settings->_slugs );
2187
+ $slugs = array_intersect_key($slugs, WPF()->settings->_slugs);
2188
+ $slugs = array_diff_key( $slugs, WPF()->tpl->templates );
2189
+ if( $slugs == array_unique( $slugs ) ){
2190
+ wpforo_update_option( 'wpforo_base_slugs', $slugs );
2191
+ WPF()->notice->add( 'Successfully Done', 'success' );
2192
+ }else{
2193
+ WPF()->notice->add( 'Please save "Forum template slugs" uniqueness', 'error' );
2194
+ }
2195
+ }
2196
+
2197
+ /*wp_safe_redirect( wp_get_raw_referer() );
2198
+ exit();*/
2199
+ }
2200
+
2201
+ /**
2202
+ * general_settings_save from submit action
2203
+ *
2204
+ * @return void
2205
+ */
2206
+ public function general_settings_save() {
2207
+ check_admin_referer( 'wpforo_settings_save_general' );
2208
+
2209
+ if( wpfkey( $_POST, 'reset' ) ) {
2210
+ wpforo_delete_option( 'wpforo_general' );
2211
+ } else {
2212
+ $general = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['general'] ), WPF()->settings->_general );
2213
+ $general['admin_bar'] = array_map( 'intval', (array) wpfval( $_POST['general'], 'admin_bar' ) );
2214
+ wpforo_update_option( 'wpforo_general', $general );
2215
+ }
2216
+
2217
+ WPF()->notice->add( 'Successfully Done', 'success' );
2218
+ wp_safe_redirect( wp_get_raw_referer() );
2219
+ exit();
2220
+ }
2221
+
2222
+ /**
2223
+ * base_slugs_settings_save from submit action
2224
+ *
2225
+ * @return void
2226
+ */
2227
+ public function slugs_settings_save() {
2228
+ check_admin_referer( 'wpforo_settings_save_board' );
2229
+
2230
+ if( wpfkey( $_POST, 'reset' ) ) {
2231
+ wpforo_delete_option( 'slugs' );
2232
+ WPF()->notice->add( 'Successfully Done', 'success' );
2233
+ } else {
2234
+ $slugs = wpforo_array_args_cast_and_merge( array_filter( array_map( 'sanitize_title', wp_unslash( $_POST['slugs'] ) ) ), WPF()->settings->_slugs );
2235
+ $slugs = array_intersect_key($slugs, WPF()->tpl->templates);
2236
+ if( $slugs == array_unique( $slugs ) ){
2237
+ foreach( $this->generate_option_names( 'slugs' ) as $option_name ) {
2238
+ wpforo_update_option( $option_name, $slugs );
2239
+ }
2240
+ WPF()->notice->add( 'Successfully Done', 'success' );
2241
+ }else{
2242
+ WPF()->notice->add( 'Please save "Forum template slugs" uniqueness', 'error' );
2243
+ }
2244
+ }
2245
+
2246
+ /*wp_safe_redirect( wp_get_raw_referer() );
2247
+ exit();*/
2248
+ }
2249
+
2250
+ /**
2251
+ * general_settings_save from submit action
2252
+ *
2253
+ * @return void
2254
+ */
2255
+ public function board_settings_save() {
2256
+ check_admin_referer( 'wpforo_settings_save_board' );
2257
+
2258
+ if( wpfkey( $_POST, 'reset' ) ) {
2259
+ wpforo_delete_option( 'board' );
2260
+ } else {
2261
+ $board = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['board'] ), WPF()->settings->_board );
2262
+ foreach( $this->generate_option_names( 'board' ) as $option_name ) {
2263
+ wpforo_update_option( $option_name, $board );
2264
+ }
2265
+ }
2266
+
2267
+ WPF()->notice->add( 'Successfully Done', 'success' );
2268
+ wp_safe_redirect( wp_get_raw_referer() );
2269
+ exit();
2270
+ }
2271
+
2272
+ /**
2273
+ * akismet_settings_save from submit action
2274
+ *
2275
+ * @return void
2276
+ */
2277
+ public function akismet_settings_save() {
2278
+ check_admin_referer( 'wpforo_settings_save_akismet' );
2279
+
2280
+ if( wpfkey( $_POST, 'reset' ) ) {
2281
+ wpforo_delete_option( 'akismet' );
2282
+ } else {
2283
+ $akismet = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['akismet'] ), WPF()->settings->_akismet );
2284
+ foreach( $this->generate_option_names( 'akismet' ) as $option_name ) {
2285
+ wpforo_update_option( $option_name, $akismet );
2286
+ }
2287
+ }
2288
+
2289
+ WPF()->notice->add( 'Successfully Done', 'success' );
2290
+ wp_safe_redirect( wp_get_raw_referer() );
2291
+ exit();
2292
+ }
2293
+
2294
+ /**
2295
+ * antispam_settings_save from submit action
2296
+ *
2297
+ * @return void
2298
+ */
2299
+ public function antispam_settings_save() {
2300
+ check_admin_referer( 'wpforo_settings_save_antispam' );
2301
+
2302
+ if( wpfkey( $_POST, 'reset' ) ) {
2303
+ wpforo_delete_option( 'antispam' );
2304
+ } else {
2305
+ $antispam = wp_unslash( $_POST['antispam'] );
2306
+ $antispam['limited_file_ext'] = array_unique( array_filter( preg_split( '#\s*\|\s*|\s*,\s*|\s+#', trim( sanitize_textarea_field( (string) wpfval( $antispam, 'limited_file_ext' ) ) ) ) ) );
2307
+ $antispam['exclude_file_ext'] = array_unique( array_filter( preg_split( '#\s*\|\s*|\s*,\s*|\s+#', trim( sanitize_textarea_field( (string) wpfval( $antispam, 'exclude_file_ext' ) ) ) ) ) );
2308
+ $antispam = wpforo_array_args_cast_and_merge( $antispam, WPF()->settings->_antispam );
2309
+ foreach( $this->generate_option_names( 'antispam' ) as $option_name ) {
2310
+ wpforo_update_option( $option_name, $antispam );
2311
+ }
2312
+ }
2313
+
2314
+ WPF()->notice->add( 'Successfully Done', 'success' );
2315
+ wp_safe_redirect( wp_get_raw_referer() );
2316
+ exit();
2317
+ }
2318
+
2319
+ /**
2320
+ * authorization_settings_save after submit action
2321
+ *
2322
+ * @return void
2323
+ */
2324
+ public function authorization_settings_save() {
2325
+ check_admin_referer( 'wpforo_settings_save_authorization' );
2326
+
2327
+ if( wpfkey( $_POST, 'reset' ) ) {
2328
+ wpforo_delete_option( 'wpforo_authorization' );
2329
+ } else {
2330
+ $authorization = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['authorization'] ), WPF()->settings->_authorization );
2331
+ if( preg_match( '#^https?://[^\r\n\t\s\0]+#isu', $authorization['login_url'] ) ) $authorization['login_url'] = '';
2332
+ if( preg_match( '#^https?://[^\r\n\t\s\0]+#isu', $authorization['register_url'] ) ) $authorization['register_url'] = '';
2333
+ if( preg_match( '#^https?://[^\r\n\t\s\0]+#isu', $authorization['lost_password_url'] ) ) $authorization['lost_password_url'] = '';
2334
+
2335
+ if( ! preg_match( '#^https?://[^\r\n\t\s\0]+#isu', $authorization['redirect_url_after_login'] ) ) $authorization['redirect_url_after_login'] = '';
2336
+ if( ! preg_match( '#^https?://[^\r\n\t\s\0]+#isu', $authorization['redirect_url_after_register'] ) ) $authorization['redirect_url_after_register'] = '';
2337
+ if( ! preg_match( '#^https?://[^\r\n\t\s\0]+#isu', $authorization['redirect_url_after_confirm_sbscrb'] ) ) $authorization['redirect_url_after_confirm_sbscrb'] = '';
2338
+
2339
+ $authorization['login_url'] = esc_url_raw( $authorization['login_url'] );
2340
+ $authorization['register_url'] = esc_url_raw( $authorization['register_url'] );
2341
+ $authorization['lost_password_url'] = esc_url_raw( $authorization['lost_password_url'] );
2342
+ $authorization['redirect_url_after_login'] = esc_url_raw( $authorization['redirect_url_after_login'] );
2343
+ $authorization['redirect_url_after_register'] = esc_url_raw( $authorization['redirect_url_after_register'] );
2344
+ $authorization['redirect_url_after_confirm_sbscrb'] = esc_url_raw( $authorization['redirect_url_after_confirm_sbscrb'] );
2345
+ $authorization['fb_api_id'] = sanitize_text_field( $authorization['fb_api_id'] );
2346
+ $authorization['fb_api_secret'] = sanitize_text_field( $authorization['fb_api_secret'] );
2347
+ $authorization['fb_redirect_url'] = esc_url_raw( $authorization['fb_redirect_url'] );
2348
+
2349
+ wpforo_update_option( 'wpforo_authorization', $authorization );
2350
+ }
2351
+
2352
+ WPF()->notice->add( 'Successfully Done', 'success' );
2353
+ wp_safe_redirect( wp_get_raw_referer() );
2354
+ exit();
2355
+ }
2356
+
2357
+ /**
2358
+ * buddypress_settings_save from submit action
2359
+ *
2360
+ * @return void
2361
+ */
2362
+ public function buddypress_settings_save() {
2363
+ check_admin_referer( 'wpforo_settings_save_buddypress' );
2364
+
2365
+ if( wpfkey( $_POST, 'reset' ) ) {
2366
+ wpforo_delete_option( 'wpforo_buddypress' );
2367
+ } else {
2368
+ $buddypress = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['buddypress'] ), WPF()->settings->_buddypress );
2369
+ wpforo_update_option( 'wpforo_buddypress', $buddypress );
2370
+ }
2371
+
2372
+ WPF()->notice->add( 'Successfully Done', 'success' );
2373
+ wp_safe_redirect( wp_get_raw_referer() );
2374
+ exit();
2375
+ }
2376
+
2377
+ /**
2378
+ * components_settings_save from submit action
2379
+ *
2380
+ * @return void
2381
+ */
2382
+ public function components_settings_save() {
2383
+ check_admin_referer( 'wpforo_settings_save_components' );
2384
+
2385
+ if( wpfkey( $_POST, 'reset' ) ) {
2386
+ wpforo_delete_option( 'components' );
2387
+ } else {
2388
+ $components = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['components'] ), WPF()->settings->_components );
2389
+ foreach( $this->generate_option_names( 'components' ) as $option_name ) {
2390
+ wpforo_update_option( $option_name, $components );
2391
+ }
2392
+ }
2393
+
2394
+ WPF()->notice->add( 'Successfully Done', 'success' );
2395
+ wp_safe_redirect( wp_get_raw_referer() );
2396
+ exit();
2397
+ }
2398
+
2399
+ /**
2400
+ * email_settings_save from submit action
2401
+ *
2402
+ * @return void
2403
+ */
2404
+ public function email_settings_save() {
2405
+ check_admin_referer( 'wpforo_settings_save_email' );
2406
+
2407
+ if( wpfkey( $_POST, 'reset' ) ) {
2408
+ wpforo_delete_option( 'wpforo_email' );
2409
+ } else {
2410
+ $email = wp_unslash( $_POST['email'] );
2411
+ $email['admin_emails'] = sanitize_text_field( $email['admin_emails'] );
2412
+ $email['admin_emails'] = array_map( 'sanitize_email', preg_split('#\s*,\s*#u', trim($email['admin_emails'])) );
2413
+ $email['admin_emails'] = array_filter( $email['admin_emails'] );
2414
+ if( !$email['admin_emails'] ) $email['admin_emails'] = (array) get_option( 'admin_email' );
2415
+ $email = wpforo_array_args_cast_and_merge( $email, WPF()->settings->_email );
2416
+
2417
+ $email['from_name'] = sanitize_text_field( $email['from_name'] );
2418
+ $email['from_email'] = sanitize_text_field( $email['from_email'] );
2419
+ $email['report_email_subject'] = sanitize_text_field( $email['report_email_subject'] );
2420
+ $email['report_email_message'] = wpforo_kses( $email['report_email_message'], 'email' );
2421
+ $email['wp_new_user_notification_email_admin_subject'] = sanitize_text_field( $email['wp_new_user_notification_email_admin_subject'] );
2422
+ $email['wp_new_user_notification_email_admin_message'] = wpforo_kses( $email['wp_new_user_notification_email_admin_message'], 'email' );
2423
+ $email['wp_new_user_notification_email_subject'] = sanitize_text_field( $email['wp_new_user_notification_email_subject'] );
2424
+ $email['wp_new_user_notification_email_message'] = wpforo_kses( $email['wp_new_user_notification_email_message'], 'email' );
2425
+ $email['reset_password_email_message'] = wpforo_kses( $email['reset_password_email_message'], 'email' );
2426
+
2427
+ wpforo_update_option( 'wpforo_email', $email );
2428
+ }
2429
+
2430
+ WPF()->notice->add( 'Successfully Done', 'success' );
2431
+ wp_safe_redirect( wp_get_raw_referer() );
2432
+ exit();
2433
+ }
2434
+
2435
+ /**
2436
+ * forums_settings_save from submit action
2437
+ *
2438
+ * @return void
2439
+ */
2440
+ public function forums_settings_save() {
2441
+ check_admin_referer( 'wpforo_settings_save_forums' );
2442
+
2443
+ if( wpfkey( $_POST, 'reset' ) ) {
2444
+ wpforo_delete_option( 'forums' );
2445
+ } else {
2446
+ $forums = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['forums'] ), WPF()->settings->_forums );
2447
+ foreach( $this->generate_option_names( 'forums' ) as $option_name ) {
2448
+ wpforo_update_option( $option_name, $forums );
2449
+ }
2450
+ }
2451
+
2452
+ WPF()->notice->add( 'Successfully Done', 'success' );
2453
+ wp_safe_redirect( wp_get_raw_referer() );
2454
+ exit();
2455
+ }
2456
+
2457
+ /**
2458
+ * logging_settings_save from submit action
2459
+ *
2460
+ * @return void
2461
+ */
2462
+ public function logging_settings_save() {
2463
+ check_admin_referer( 'wpforo_settings_save_logging' );
2464
+
2465
+ if( wpfkey( $_POST, 'reset' ) ) {
2466
+ wpforo_delete_option( 'logging' );
2467
+ } else {
2468
+ $logging = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['logging'] ), WPF()->settings->_logging );
2469
+ foreach( $this->generate_option_names( 'logging' ) as $option_name ) {
2470
+ wpforo_update_option( $option_name, $logging );
2471
+ }
2472
+ }
2473
+
2474
+ WPF()->notice->add( 'Successfully Done', 'success' );
2475
+ wp_safe_redirect( wp_get_raw_referer() );
2476
+ exit();
2477
+ }
2478
+
2479
+ /**
2480
+ * members_settings_save from submit action
2481
+ *
2482
+ * @return void
2483
+ */
2484
+ public function members_settings_save() {
2485
+ check_admin_referer( 'wpforo_settings_save_members' );
2486
+
2487
+ if( wpfkey( $_POST, 'reset' ) ) {
2488
+ wpforo_delete_option( 'wpforo_members' );
2489
+ } else {
2490
+ $members = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['members'] ), WPF()->settings->_members );
2491
+ wpforo_update_option( 'wpforo_members', $members );
2492
+ }
2493
+
2494
+ WPF()->notice->add( 'Successfully Done', 'success' );
2495
+ wp_safe_redirect( wp_get_raw_referer() );
2496
+ exit();
2497
+ }
2498
+
2499
+ /**
2500
+ * notifications_settings_save from submit action
2501
+ *
2502
+ * @return void
2503
+ */
2504
+ public function notifications_settings_save() {
2505
+ check_admin_referer( 'wpforo_settings_save_notifications' );
2506
+
2507
+ if( wpfkey( $_POST, 'reset' ) ) {
2508
+ wpforo_delete_option( 'notifications' );
2509
+ } else {
2510
+ $notifications = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['notifications'] ), WPF()->settings->_notifications );
2511
+ foreach( $this->generate_option_names( 'notifications' ) as $option_name ) {
2512
+ wpforo_update_option( $option_name, $notifications );
2513
+ }
2514
+ }
2515
+
2516
+ WPF()->notice->add( 'Successfully Done', 'success' );
2517
+ wp_safe_redirect( wp_get_raw_referer() );
2518
+ exit();
2519
+ }
2520
+
2521
+ /**
2522
+ * posting_settings_save from submit action
2523
+ *
2524
+ * @return void
2525
+ */
2526
+ public function posting_settings_save() {
2527
+ check_admin_referer( 'wpforo_settings_save_posting' );
2528
+
2529
+ if( wpfkey( $_POST, 'reset' ) ) {
2530
+ wpforo_delete_option( 'posting' );
2531
+ } else {
2532
+ $posting = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['posting'] ), WPF()->settings->_posting );
2533
+ $posting['max_upload_size'] = $posting['max_upload_size'] * 1024 * 1024;
2534
+ $posting['edit_own_topic_durr'] = $posting['edit_own_topic_durr'] * 60;
2535
+ $posting['delete_own_topic_durr'] = $posting['delete_own_topic_durr'] * 60;
2536
+ $posting['edit_own_post_durr'] = $posting['edit_own_post_durr'] * 60;
2537
+ $posting['delete_own_post_durr'] = $posting['delete_own_post_durr'] * 60;
2538
+ $posting['extra_html_tags'] = sanitize_textarea_field( $posting['extra_html_tags'] );
2539
+ foreach( $this->generate_option_names( 'posting' ) as $option_name ) {
2540
+ wpforo_update_option( $option_name, $posting );
2541
+ }
2542
+ }
2543
+
2544
+ WPF()->notice->add( 'Successfully Done', 'success' );
2545
+ wp_safe_redirect( wp_get_raw_referer() );
2546
+ exit();
2547
+ }
2548
+
2549
+ /**
2550
+ * profiles_settings_save from submit action
2551
+ *
2552
+ * @return void
2553
+ */
2554
+ public function profiles_settings_save() {
2555
+ check_admin_referer( 'wpforo_settings_save_profiles' );
2556
+
2557
+ if( wpfkey( $_POST, 'reset' ) ) {
2558
+ wpforo_delete_option( 'wpforo_profiles' );
2559
+ } else {
2560
+ $profiles = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['profiles'] ), WPF()->settings->_profiles );
2561
+ $profiles['default_cover'] = WPF()->settings->profiles['default_cover'];
2562
+ $profiles['default_title'] = sanitize_text_field( $profiles['default_title'] );
2563
+ $profiles['online_status_timeout'] = $profiles['online_status_timeout'] * 60;
2564
+ $profiles['title_groupids'] = array_map( 'intval', (array) wpfval( $_POST['profiles'], 'title_groupids' ) );
2565
+ $profiles['title_secondary_groupids'] = array_map( 'intval', (array) wpfval( $_POST['profiles'], 'title_secondary_groupids' ) );
2566
+ wpforo_update_option( 'wpforo_profiles', $profiles );
2567
+ }
2568
+
2569
+ WPF()->notice->add( 'Successfully Done', 'success' );
2570
+ wp_safe_redirect( wp_get_raw_referer() );
2571
+ exit();
2572
+ }
2573
+
2574
+ /**
2575
+ * rating_settings_save from submit action
2576
+ *
2577
+ * @return void
2578
+ */
2579
+ public function rating_settings_save() {
2580
+ check_admin_referer( 'wpforo_settings_save_rating' );
2581
+
2582
+ if( wpfkey( $_POST, 'reset' ) ) {
2583
+ wpforo_delete_option( 'wpforo_rating' );
2584
+ } else {
2585
+ $rating = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['rating'] ), WPF()->settings->_rating );
2586
+ $rating['rating_title_ug'] = array_map( 'intval', (array) wpfval( $_POST['rating'], 'rating_title_ug' ) );
2587
+ $rating['rating_badge_ug'] = array_map( 'intval', (array) wpfval( $_POST['rating'], 'rating_badge_ug' ) );
2588
+ wpforo_update_option( 'wpforo_rating', $rating );
2589
+ }
2590
+
2591
+ WPF()->notice->add( 'Successfully Done', 'success' );
2592
+ wp_safe_redirect( wp_get_raw_referer() );
2593
+ exit();
2594
+ }
2595
+
2596
+ /**
2597
+ * recaptcha_settings_save from submit action
2598
+ *
2599
+ * @return void
2600
+ */
2601
+ public function recaptcha_settings_save() {
2602
+ check_admin_referer( 'wpforo_settings_save_recaptcha' );
2603
+
2604
+ if( wpfkey( $_POST, 'reset' ) ) {
2605
+ wpforo_delete_option( 'wpforo_recaptcha' );
2606
+ } else {
2607
+ $recaptcha = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['recaptcha'] ), WPF()->settings->_recaptcha );
2608
+ $recaptcha['site_key'] = sanitize_text_field( $recaptcha['site_key'] );
2609
+ $recaptcha['secret_key'] = sanitize_text_field( $recaptcha['secret_key'] );
2610
+ wpforo_update_option( 'wpforo_recaptcha', $recaptcha );
2611
+ }
2612
+
2613
+ WPF()->notice->add( 'Successfully Done', 'success' );
2614
+ wp_safe_redirect( wp_get_raw_referer() );
2615
+ exit();
2616
+ }
2617
+
2618
+ /**
2619
+ * rss_settings_save from submit action
2620
+ *
2621
+ * @return void
2622
+ */
2623
+ public function rss_settings_save() {
2624
+ check_admin_referer( 'wpforo_settings_save_rss' );
2625
+
2626
+ if( wpfkey( $_POST, 'reset' ) ) {
2627
+ wpforo_delete_option( 'rss' );
2628
+ } else {
2629
+ $rss = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['rss'] ), WPF()->settings->_rss );
2630
+ foreach( $this->generate_option_names( 'rss' ) as $option_name ) {
2631
+ wpforo_update_option( $option_name, $rss );
2632
+ }
2633
+ }
2634
+
2635
+ WPF()->notice->add( 'Successfully Done', 'success' );
2636
+ wp_safe_redirect( wp_get_raw_referer() );
2637
+ exit();
2638
+ }
2639
+
2640
+ /**
2641
+ * seo_settings_save from submit action
2642
+ *
2643
+ * @return void
2644
+ */
2645
+ public function seo_settings_save() {
2646
+ check_admin_referer( 'wpforo_settings_save_seo' );
2647
+
2648
+ if( wpfkey( $_POST, 'reset' ) ) {
2649
+ wpforo_delete_option( 'seo' );
2650
+ } else {
2651
+ $seo = wp_unslash( $_POST['seo'] );
2652
+ $seo['dofollow'] = array_filter( preg_split( '#\s+#', sanitize_textarea_field( (string) wpfval( $seo, 'dofollow' ) ) ) );
2653
+ $seo['noindex'] = array_filter( preg_split( '#\s+#', sanitize_textarea_field( (string) wpfval( $seo, 'noindex' ) ) ) );
2654
+ $seo['noindex'] = array_map( 'esc_url_raw', $seo['noindex'] );
2655
+ $seo = wpforo_array_args_cast_and_merge( $seo, WPF()->settings->_seo );
2656
+ foreach( $this->generate_option_names( 'seo' ) as $option_name ) {
2657
+ wpforo_update_option( $option_name, $seo );
2658
+ }
2659
+ wpforo_clean_cache( 'forum-soft' );
2660
+ }
2661
+
2662
+ WPF()->notice->add( 'Successfully Done', 'success' );
2663
+ wp_safe_redirect( wp_get_raw_referer() );
2664
+ exit();
2665
+ }
2666
+
2667
+ /**
2668
+ * social_settings_save from submit action
2669
+ *
2670
+ * @return void
2671
+ */
2672
+ public function social_settings_save() {
2673
+ check_admin_referer( 'wpforo_settings_save_social' );
2674
+
2675
+ if( wpfkey( $_POST, 'reset' ) ) {
2676
+ wpforo_delete_option( 'social' );
2677
+ } else {
2678
+ $social = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['social'] ), WPF()->settings->_social );
2679
+ $social['sb'] = wpforo_array_args_cast_and_merge( (array) wpfval( $_POST['social'], 'sb' ), array_map( '__return_false', WPF()->settings->_social['sb'] ) );
2680
+ $social['sb_location'] = wpforo_array_args_cast_and_merge( (array) wpfval( $_POST['social'], 'sb_location' ), array_map( '__return_false', WPF()->settings->_social['sb_location'] ) );
2681
+ foreach( $this->generate_option_names( 'social' ) as $option_name ) {
2682
+ wpforo_update_option( $option_name, $social );
2683
+ }
2684
+ }
2685
+
2686
+ WPF()->notice->add( 'Successfully Done', 'success' );
2687
+ wp_safe_redirect( wp_get_raw_referer() );
2688
+ exit();
2689
+ }
2690
+
2691
+ /**
2692
+ * styles_settings_save from submit action
2693
+ *
2694
+ * @return void
2695
+ */
2696
+ public function styles_settings_save() {
2697
+ check_admin_referer( 'wpforo_settings_save_styles' );
2698
+
2699
+ if( wpfkey( $_POST, 'reset' ) ) {
2700
+ wpforo_delete_option( 'styles_' . WPF()->tpl->theme );
2701
+ } else {
2702
+ $styles = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['styles'] ), WPF()->settings->_styles );
2703
+ $styles['custom_css'] = sanitize_textarea_field( $styles['custom_css'] );
2704
+ foreach( $this->generate_option_names( 'styles_' . WPF()->tpl->theme ) as $option_name ) {
2705
+ wpforo_update_option( $option_name, $styles );
2706
+ }
2707
+ }
2708
+
2709
+ WPF()->notice->add( 'Successfully Done', 'success' );
2710
+ wp_safe_redirect( wp_get_raw_referer() );
2711
+ exit();
2712
+ }
2713
+
2714
+ /**
2715
+ * tags_settings_save from submit action
2716
+ *
2717
+ * @return void
2718
+ */
2719
+ public function tags_settings_save() {
2720
+ check_admin_referer( 'wpforo_settings_save_tags' );
2721
+
2722
+ if( wpfkey( $_POST, 'reset' ) ) {
2723
+ wpforo_delete_option( 'tags' );
2724
+ } else {
2725
+ $tags = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['tags'] ), WPF()->settings->_tags );
2726
+ foreach( $this->generate_option_names( 'tags' ) as $option_name ) {
2727
+ wpforo_update_option( $option_name, $tags );
2728
+ }
2729
+ }
2730
+
2731
+ WPF()->notice->add( 'Successfully Done', 'success' );
2732
+ wp_safe_redirect( wp_get_raw_referer() );
2733
+ exit();
2734
+ }
2735
+
2736
+ /**
2737
+ * topics_settings_save from submit action
2738
+ *
2739
+ * @return void
2740
+ */
2741
+ public function topics_settings_save() {
2742
+ check_admin_referer( 'wpforo_settings_save_topics' );
2743
+
2744
+ if( wpfkey( $_POST, 'reset' ) ) {
2745
+ wpforo_delete_option( 'topics' );
2746
+ } else {
2747
+ $topics = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['topics'] ), WPF()->settings->_topics );
2748
+ foreach( $this->generate_option_names( 'topics' ) as $option_name ) {
2749
+ wpforo_update_option( $option_name, $topics );
2750
+ }
2751
+ }
2752
+
2753
+ WPF()->notice->add( 'Successfully Done', 'success' );
2754
+ wp_safe_redirect( wp_get_raw_referer() );
2755
+ exit();
2756
+ }
2757
+
2758
+ /**
2759
+ * um_settings_save from submit action
2760
+ *
2761
+ * @return void
2762
+ */
2763
+ public function um_settings_save() {
2764
+ check_admin_referer( 'wpforo_settings_save_um' );
2765
+
2766
+ if( wpfkey( $_POST, 'reset' ) ) {
2767
+ wpforo_delete_option( 'wpforo_um' );
2768
+ } else {
2769
+ $um = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['um'] ), WPF()->settings->_um );
2770
+ wpforo_update_option( 'wpforo_um', $um );
2771
+ }
2772
+
2773
+ WPF()->notice->add( 'Successfully Done', 'success' );
2774
+ wp_safe_redirect( wp_get_raw_referer() );
2775
+ exit();
2776
+ }
2777
+
2778
+ /**
2779
+ * um_settings_save from submit action
2780
+ *
2781
+ * @return void
2782
+ */
2783
+ public function legal_settings_save() {
2784
+ check_admin_referer( 'wpforo_settings_save_legal' );
2785
+
2786
+ if( wpfkey( $_POST, 'reset' ) ) {
2787
+ wpforo_delete_option( 'wpforo_legal' );
2788
+ } else {
2789
+ $legal = wpforo_array_args_cast_and_merge( wp_unslash( $_POST['legal'] ), WPF()->settings->_legal );
2790
+ wpforo_update_option( 'wpforo_legal', $legal );
2791
+ }
2792
+
2793
+ WPF()->notice->add( 'Successfully Done', 'success' );
2794
+ wp_safe_redirect( wp_get_raw_referer() );
2795
+ exit();
2796
+ }
2797
+
2798
+ /**
2799
+ * @return array with all boardids where need to save options
2800
+ */
2801
+ private function get_boardids_to_be_saved() {
2802
+ $boardids = (array) WPF()->board->get_current( 'boardid' );
2803
+ if( wpfkey( $_POST, 'save_for_all' ) ) {
2804
+ $boardids = array_unique( array_merge( $boardids, WPF()->board->get_active_boardids() ) );
2805
+ }
2806
+
2807
+ return $boardids;
2808
+ }
2809
+
2810
+ /**
2811
+ * @param string $basename
2812
+ *
2813
+ * @return string[]
2814
+ */
2815
+ public function generate_option_names( $basename ) {
2816
+ return array_map(
2817
+ function( $boardid ) use ( $basename ) {
2818
+ return 'wpforo_' . ( $boardid ? $boardid . '_' : '' ) . $basename;
2819
+ },
2820
+ $this->get_boardids_to_be_saved()
2821
+ );
2822
+ }
2823
+
2824
+ /**
2825
+ * uninstall all wpforo
2826
+ */
2827
+ public function uninstall() {
2828
+ check_admin_referer( 'wpforo_uninstall' );
2829
+ if( current_user_can('administrator') && current_user_can( 'activate_plugins' ) ){
2830
+ wpforo_uninstall();
2831
+ }
2832
+ wp_safe_redirect( wp_get_referer() );
2833
+ exit();
2834
+ }
2835
+ }
classes/Activity.php ADDED
@@ -0,0 +1,683 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ use stdClass;
6
+
7
+ // Exit if accessed directly
8
+ if( ! defined( 'ABSPATH' ) ) exit;
9
+
10
+ class Activity {
11
+ private $default;
12
+ public $activity;
13
+ private $actions;
14
+ public $notifications = [];
15
+
16
+ public function __construct() {
17
+ add_action( 'wpforo_after_init', [ $this, 'init' ] );
18
+ }
19
+
20
+ public function init() {
21
+ $this->init_defaults();
22
+ $this->activity = $this->default->activity;
23
+ $this->init_hooks();
24
+ $this->init_actions();
25
+ if( is_user_logged_in() && wpforo_setting( 'notifications', 'notifications' ) ) {
26
+ $this->notifications = $this->get_notifications();
27
+ }
28
+ }
29
+
30
+ private function init_actions() {
31
+ $this->actions = [
32
+ 'edit_topic' => [
33
+ 'title' => wpforo_phrase( 'Edit Topic', false ),
34
+ 'icon' => '',
35
+ 'description' => wpforo_phrase( 'This topic was modified %s by %s', false ),
36
+ 'before' => '<div class="wpf-post-edited"><i class="far fa-edit"></i>',
37
+ 'after' => '</div>',
38
+ ],
39
+ 'edit_post' => [
40
+ 'title' => wpforo_phrase( 'Edit Post', false ),
41
+ 'icon' => '',
42
+ 'description' => wpforo_phrase( 'This post was modified %s by %s', false ),
43
+ 'before' => '<div class="wpf-post-edited"><i class="far fa-edit"></i>',
44
+ 'after' => '</div>',
45
+ ],
46
+ 'new_reply' => [
47
+ 'title' => wpforo_phrase( 'New Reply', false ),
48
+ 'icon' => '<i class="fas fa-reply fa-rotate-180"></i>',
49
+ 'description' => wpforo_phrase( 'New reply from %1$s, %2$s', false ),
50
+ 'before' => '<li class="wpf-new_reply">',
51
+ 'after' => '</li>',
52
+ ],
53
+ 'new_like' => [
54
+ 'title' => wpforo_phrase( 'New Like', false ),
55
+ 'icon' => '<i class="fas fa-heart"></i>',
56
+ 'description' => wpforo_phrase( 'New like from %1$s, %2$s', false ),
57
+ 'before' => '<li class="wpf-new_like">',
58
+ 'after' => '</li>',
59
+ ],
60
+ 'new_up_vote' => [
61
+ 'title' => wpforo_phrase( 'New Up Vote', false ),
62
+ 'icon' => '<i class="fas fa-arrow-alt-circle-up"></i>',
63
+ 'description' => wpforo_phrase( 'New up vote from %1$s, %2$s', false ),
64
+ 'before' => '<li class="wpf-new_up_vote">',
65
+ 'after' => '</li>',
66
+ ],
67
+ 'new_down_vote' => [
68
+ 'title' => wpforo_phrase( 'New Down Vote', false ),
69
+ 'icon' => '<i class="fas fa-arrow-alt-circle-down"></i>',
70
+ 'description' => wpforo_phrase( 'New down vote from %1$s, %2$s', false ),
71
+ 'before' => '<li class="wpf-new_down_vote">',
72
+ 'after' => '</li>',
73
+ ],
74
+ 'new_mention' => [
75
+ 'title' => wpforo_phrase( 'New User Mentioning', false ),
76
+ 'icon' => '<i class="fas fa-at"></i>',
77
+ 'description' => wpforo_phrase( '%1$s has mentioned you, %2$s', false ),
78
+ 'before' => '<li class="wpf-new_mention">',
79
+ 'after' => '</li>',
80
+ ],
81
+ 'default' => [
82
+ 'title' => wpforo_phrase( 'New Notification', false ),
83
+ 'icon' => '<i class="fas fa-bell"></i>',
84
+ 'description' => wpforo_phrase( 'New notification from %1$s, %2$s', false ),
85
+ 'before' => '<li class="wpf-new_note">',
86
+ 'after' => '</li>',
87
+ ],
88
+ ];
89
+
90
+ $this->actions = apply_filters( 'wpforo_register_actions', $this->actions );
91
+ }
92
+
93
+ private function init_defaults() {
94
+ $this->default = new stdClass();
95
+ $this->default->activity = [
96
+ 'id' => 0,
97
+ 'type' => '',
98
+ 'itemid' => 0,
99
+ 'itemtype' => '',
100
+ 'itemid_second' => 0,
101
+ 'userid' => 0,
102
+ 'name' => '',
103
+ 'email' => '',
104
+ 'date' => 0,
105
+ 'content' => '',
106
+ 'permalink' => '',
107
+ 'new' => 0,
108
+ ];
109
+ $this->default->activity_format = [
110
+ 'id' => '%d',
111
+ 'type' => '%s',
112
+ 'itemid' => '%d',
113
+ 'itemtype' => '%s',
114
+ 'itemid_second' => '%d',
115
+ 'userid' => '%d',
116
+ 'name' => '%s',
117
+ 'email' => '%s',
118
+ 'date' => '%d',
119
+ 'content' => '%s',
120
+ 'permalink' => '%s',
121
+ 'new' => '%d',
122
+ ];
123
+ $this->default->sql_select_args = [
124
+ 'type' => null,
125
+ 'userid' => null,
126
+ 'itemtype' => null,
127
+ 'new' => null,
128
+ 'include' => [],
129
+ 'exclude' => [],
130
+ 'userids_include' => [],
131
+ 'userids_exclude' => [],
132
+ 'types_include' => [],
133
+ 'types_exclude' => [],
134
+ 'itemids_include' => [],
135
+ 'itemids_exclude' => [],
136
+ 'itemtypes_include' => [],
137
+ 'itemtypes_exclude' => [],
138
+ 'emails_include' => [],
139
+ 'emails_exclude' => [],
140
+ 'orderby' => 'id',
141
+ 'order' => 'ASC',
142
+ 'offset' => null,
143
+ 'row_count' => null,
144
+ ];
145
+ }
146
+
147
+ private function init_hooks() {
148
+ if( wpforo_setting( 'posting', 'edit_topic' ) ) {
149
+ add_action( 'wpforo_after_edit_topic', [ $this, 'after_edit_topic' ] );
150
+ }
151
+ if( wpforo_setting( 'posting', 'edit_post' ) ) {
152
+ add_action( 'wpforo_after_edit_post', [ $this, 'after_edit_post' ] );
153
+ }
154
+
155
+ if( WPF()->current_userid && wpforo_setting( 'notifications', 'notifications' ) ) {
156
+ if( wpforo_setting( 'notifications', 'notifications_bar' ) ) {
157
+ add_action( 'wpforo_before_search_toggle', [ $this, 'bell' ] );
158
+ }
159
+ add_action( 'wpforo_after_add_post', [ $this, 'after_add_post' ], 10, 2 );
160
+ add_action( 'wpforo_post_status_update', [ $this, 'update_notification' ], 10, 2 );
161
+ add_action( 'wpforo_like', [ $this, 'after_like' ] );
162
+ add_action( 'wpforo_dislike', [ $this, 'after_dislike' ] );
163
+ add_action( 'wpforo_vote', [ $this, 'after_vote' ], 10, 2 );
164
+ }
165
+ }
166
+
167
+ private function filter_built_html_rows( $rows ) {
168
+ $_rows = [];
169
+ foreach( $rows as $row_key => $row ) {
170
+ $in_array = false;
171
+ if( $_rows ) {
172
+ foreach( $_rows as $_row_key => $_row ) {
173
+ if( in_array( $row, $_row ) ) {
174
+ $in_array = true;
175
+ $match_key = $_row_key;
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ if( $in_array && isset( $match_key ) ) {
181
+ $_rows[ $match_key ]['times'] ++;
182
+ } else {
183
+ $_rows[ $row_key ]['html'] = $row;
184
+ $_rows[ $row_key ]['times'] = 1;
185
+ }
186
+ }
187
+
188
+ $rows = [];
189
+ foreach( $_rows as $_row ) {
190
+ $times = '';
191
+ if( $_row['times'] > 1 ) {
192
+ $times = ' ' . sprintf(
193
+ wpforo_phrase( '%d times', false ),
194
+ $_row['times']
195
+ );
196
+ }
197
+
198
+ $rows[] = sprintf( $_row['html'], $times );
199
+ }
200
+
201
+ $limit = wpforo_setting( 'posting', 'edit_log_display_limit' );
202
+ if( $limit ) $rows = array_slice( $rows, ( - 1 * $limit ), $limit );
203
+
204
+ return $rows;
205
+ }
206
+
207
+ private function parse_activity( $data ) {
208
+ return array_merge( $this->default->activity, $data );
209
+ }
210
+
211
+ private function parse_args( $args ) {
212
+ $args = wpforo_parse_args( $args, $this->default->sql_select_args );
213
+
214
+ $args['include'] = wpforo_parse_args( $args['include'] );
215
+ $args['exclude'] = wpforo_parse_args( $args['exclude'] );
216
+
217
+ $args['userids_include'] = wpforo_parse_args( $args['userids_include'] );
218
+ $args['userids_exclude'] = wpforo_parse_args( $args['userids_exclude'] );
219
+
220
+ $args['types_include'] = wpforo_parse_args( $args['types_include'] );
221
+ $args['types_exclude'] = wpforo_parse_args( $args['types_exclude'] );
222
+
223
+ $args['itemids_include'] = wpforo_parse_args( $args['itemids_include'] );
224
+ $args['itemids_exclude'] = wpforo_parse_args( $args['itemids_exclude'] );
225
+
226
+ $args['itemtypes_include'] = wpforo_parse_args( $args['itemtypes_include'] );
227
+ $args['itemtypes_exclude'] = wpforo_parse_args( $args['itemtypes_exclude'] );
228
+
229
+ $args['emails_include'] = wpforo_parse_args( $args['emails_include'] );
230
+ $args['emails_exclude'] = wpforo_parse_args( $args['emails_exclude'] );
231
+
232
+ return $args;
233
+ }
234
+
235
+ private function build_sql_select( $args ) {
236
+ $args = $this->parse_args( $args );
237
+
238
+ $wheres = [];
239
+
240
+ if( ! is_null( $args['type'] ) ) $wheres[] = "`type` = '" . esc_sql( $args['type'] ) . "'";
241
+ if( ! is_null( $args['itemtype'] ) ) $wheres[] = "`itemtype` = '" . esc_sql( $args['itemtype'] ) . "'";
242
+ if( ! is_null( $args['userid'] ) ) $wheres[] = "`userid` = " . intval( $args['userid'] );
243
+ if( ! is_null( $args['new'] ) ) $wheres[] = "`new` = " . intval( $args['new'] );
244
+
245
+ if( ! empty( $args['include'] ) ) $wheres[] = "`id` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['include'] ) ) . ")";
246
+ if( ! empty( $args['exclude'] ) ) $wheres[] = "`id` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['exclude'] ) ) . ")";
247
+
248
+ if( ! empty( $args['userids_include'] ) ) $wheres[] = "`userid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['userids_include'] ) ) . ")";
249
+ if( ! empty( $args['userids_exclude'] ) ) $wheres[] = "`userid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['userids_exclude'] ) ) . ")";
250
+
251
+ if( ! empty( $args['types_include'] ) ) $wheres[] = "`type` IN('" . implode( "','", array_map( 'trim', $args['types_include'] ) ) . "')";
252
+ if( ! empty( $args['types_exclude'] ) ) $wheres[] = "`type` NOT IN('" . implode( "','", array_map( 'trim', $args['types_exclude'] ) ) . "')";
253
+
254
+ if( ! empty( $args['itemids_include'] ) ) $wheres[] = "`itemid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['itemids_include'] ) ) . ")";
255
+ if( ! empty( $args['itemids_exclude'] ) ) $wheres[] = "`itemid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['itemids_exclude'] ) ) . ")";
256
+
257
+ if( ! empty( $args['itemtypes_include'] ) ) $wheres[] = "`itemtype` IN('" . implode( "','", array_map( 'trim', $args['itemtypes_include'] ) ) . "')";
258
+ if( ! empty( $args['itemtypes_exclude'] ) ) $wheres[] = "`itemtype` NOT IN('" . implode( "','", array_map( 'trim', $args['itemtypes_exclude'] ) ) . "')";
259
+
260
+ if( ! empty( $args['emails_include'] ) ) $wheres[] = "`email` IN('" . implode( "','", array_map( 'trim', $args['emails_include'] ) ) . "')";
261
+ if( ! empty( $args['emails_exclude'] ) ) $wheres[] = "`email` NOT IN('" . implode( "','", array_map( 'trim', $args['emails_exclude'] ) ) . "')";
262
+
263
+ $sql = "SELECT * FROM " . WPF()->tables->activity;
264
+ if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
265
+ $sql .= " ORDER BY " . $args['orderby'] . " " . $args['order'];
266
+ if( $args['row_count'] ) {
267
+ if( ! empty( $args['offset'] ) ) {
268
+ $sql .= " LIMIT " . wpforo_bigintval( $args['offset'] ) . "," . wpforo_bigintval( $args['row_count'] );
269
+ } else {
270
+ $sql .= " LIMIT " . wpforo_bigintval( $args['row_count'] );
271
+ }
272
+ }
273
+
274
+ return $sql;
275
+ }
276
+
277
+ public function get_activity( $args ) {
278
+ if( ! $args ) return false;
279
+
280
+ return $this->parse_activity( WPF()->db->get_row( $this->build_sql_select( $args ), ARRAY_A ) );
281
+ }
282
+
283
+ public function get_activities( $args ) {
284
+ if( ! $args ) return [];
285
+
286
+ return array_map( [ $this, 'parse_activity' ], (array) WPF()->db->get_results( $this->build_sql_select( $args ), ARRAY_A ) );
287
+ }
288
+
289
+ public function after_edit_topic( $topic ) {
290
+ $data = [
291
+ 'type' => 'edit_topic',
292
+ 'itemid' => $topic['topicid'],
293
+ 'itemtype' => 'topic',
294
+ 'userid' => WPF()->current_userid,
295
+ 'name' => WPF()->current_user_display_name,
296
+ 'email' => WPF()->current_user_email,
297
+ 'permalink' => wpforo_topic( $topic['topicid'], 'url' ),
298
+ ];
299
+
300
+ $this->add( $data );
301
+ }
302
+
303
+ public function after_edit_post( $post ) {
304
+ $data = [
305
+ 'type' => 'edit_post',
306
+ 'itemid' => $post['postid'],
307
+ 'itemtype' => 'post',
308
+ 'userid' => WPF()->current_userid,
309
+ 'name' => WPF()->current_user_display_name,
310
+ 'email' => WPF()->current_user_email,
311
+ 'permalink' => wpforo_post( $post['postid'], 'url' ),
312
+ ];
313
+
314
+ $this->add( $data );
315
+ }
316
+
317
+ public function after_add_post( $post, $topic ) {
318
+ $this->add_notification_new_reply( 'new_reply', $post, $topic );
319
+ }
320
+
321
+ private function add( $data ) {
322
+ if( empty( $data ) ) return false;
323
+ $activity = array_merge( $this->default->activity, $data );
324
+ unset( $activity['id'] );
325
+
326
+ if( ! $activity['type'] || ! $activity['itemid'] || ! $activity['itemtype'] ) return false;
327
+ if( ! $activity['date'] ) $activity['date'] = current_time( 'timestamp', 1 );
328
+
329
+ $activity = wpforo_array_ordered_intersect_key( $activity, $this->default->activity_format );
330
+ if( WPF()->db->insert(
331
+ WPF()->tables->activity,
332
+ $activity,
333
+ wpforo_array_ordered_intersect_key( $this->default->activity_format, $activity )
334
+ ) ) {
335
+ return WPF()->db->insert_id;
336
+ }
337
+
338
+ return false;
339
+ }
340
+
341
+ private function edit( $data, $where ) {
342
+ if( empty( $data ) || empty( $where ) ) return false;
343
+ if( is_numeric( $where ) ) $where = [ 'id' => $where ];
344
+ $data = (array) $data;
345
+ $where = (array) $where;
346
+
347
+ $data = wpforo_array_ordered_intersect_key( $data, $this->default->activity_format );
348
+ $where = wpforo_array_ordered_intersect_key( $where, $this->default->activity_format );
349
+ if( false !== WPF()->db->update(
350
+ WPF()->tables->activity,
351
+ $data,
352
+ $where,
353
+ wpforo_array_ordered_intersect_key( $this->default->activity_format, $data ),
354
+ wpforo_array_ordered_intersect_key( $this->default->activity_format, $where )
355
+ ) ) {
356
+ return true;
357
+ }
358
+
359
+ return false;
360
+ }
361
+
362
+ private function delete( $where ) {
363
+ if( empty( $where ) ) return false;
364
+ if( is_numeric( $where ) ) $where = [ 'id' => $where ];
365
+ $where = (array) $where;
366
+
367
+ $where = wpforo_array_ordered_intersect_key( $where, $this->default->activity_format );
368
+ if( false !== WPF()->db->delete(
369
+ WPF()->tables->activity,
370
+ $where,
371
+ wpforo_array_ordered_intersect_key( $this->default->activity_format, $where )
372
+ ) ) {
373
+ return true;
374
+ }
375
+
376
+ return false;
377
+ }
378
+
379
+ public function build( $itemtype, $itemid, $type, $echo = false ) {
380
+ $rows = [];
381
+ $args = [
382
+ 'itemtypes_include' => $itemtype,
383
+ 'itemids_include' => $itemid,
384
+ 'types_include' => $type,
385
+ ];
386
+ if( $activities = $this->get_activities( $args ) ) {
387
+ foreach( $activities as $activity ) {
388
+ switch( $activity['type'] ) {
389
+ case 'edit_topic':
390
+ case 'edit_post':
391
+ $rows[] = $this->_build_edit_topic_edit_post( $activity );
392
+ break;
393
+ }
394
+ }
395
+ }
396
+
397
+ $rows = $this->filter_built_html_rows( $rows );
398
+
399
+ $html = ( $rows ? implode( '', $rows ) : '' );
400
+ if( $echo ) echo $html;
401
+
402
+ return $html;
403
+ }
404
+
405
+ private function _build_edit_topic_edit_post( $activity ) {
406
+ $html = '';
407
+ $type = $activity['type'];
408
+ $userid = $activity['userid'];
409
+ $date = wpforo_date( $activity['date'], 'ago', false ) . '%s';
410
+
411
+ if( $userid ) {
412
+ $profile_url = wpforo_member( $userid, 'profile_url' );
413
+ $display_name = wpforo_member( $userid, 'display_name' );
414
+ $user = sprintf( '<a href="%s">%s</a>', $profile_url, $display_name );
415
+ } else {
416
+ $user = $activity['name'] ?: wpforo_phrase( 'Guest', false );
417
+ }
418
+
419
+ if( wpfval( $this->actions, $type, 'before' ) ) {
420
+ $html = $this->actions[ $type ]['before'];
421
+ $html = apply_filters( 'wpforo_activity_action_html_before', $html, $activity );
422
+ }
423
+ if( wpfval( $this->actions, $type, 'description' ) ) {
424
+ $html .= sprintf( $this->actions[ $activity['type'] ]['description'], $date, str_replace( '%', '%%', $user ) );
425
+ $html = apply_filters( 'wpforo_activity_action_html', $html, $activity );
426
+ }
427
+ if( wpfval( $this->actions, $type, 'after' ) ) {
428
+ $html .= $this->actions[ $type ]['after'];
429
+ $html = apply_filters( 'wpforo_activity_action_html_after', $html, $activity );
430
+ }
431
+
432
+ return $html;
433
+ }
434
+
435
+ public function bell( $class = 'wpf-alerts' ) {
436
+ wp_enqueue_script( 'wpforo-widgets-js' );
437
+
438
+ $class = ( ! $class ) ? 'wpf-alerts' : $class;
439
+ $count = ( ! empty( $this->notifications ) ) ? count( $this->notifications ) : 0;
440
+ $phrase = ( $count > 1 ) ? wpforo_phrase( 'You have new notifications', false ) : wpforo_phrase( 'You have a new notification', false );
441
+ $tooltip = ' wpf-tooltip="' . esc_attr( $phrase ) . '" wpf-tooltip-size="middle"';
442
+ ?>
443
+ <div class="<?php echo esc_attr( $class ) ?> <?php echo ( $count ) ? 'wpf-new' : ''; ?>">
444
+ <?php if( $count ): ?>
445
+ <div class="wpf-bell" <?php echo $tooltip ?>>
446
+ <i class="fas fa-bell"></i>
447
+ <span class="wpf-alerts-count"><?php echo $count ?></span>
448
+ </div>
449
+ <?php else: ?>
450
+ <div class="wpf-bell">
451
+ <i class="far fa-bell"></i>
452
+ </div>
453
+ <?php endif; ?>
454
+ </div>
455
+ <?php
456
+ }
457
+
458
+ public function notifications() {
459
+ ?>
460
+ <div class="wpf-notifications">
461
+ <div class="wpf-notification-head">
462
+ <i class="far fa-bell"></i> <?php wpforo_phrase( 'Notifications' ) ?>
463
+ </div>
464
+ <div class="wpf-notification-content">
465
+ <div class="wpf-nspin"><i class="fas fa-spinner fa-spin"></i></div>
466
+ </div>
467
+ <div class="wpf-notification-actions">
468
+ <span class="wpf-action wpf-notification-action-clear-all" data-foro_n="<?php echo wp_create_nonce( 'wpforo_clear_notifications' ) ?>"><?php wpforo_phrase( 'Clear all' ) ?></span>
469
+ </div>
470
+ </div>
471
+ <?php
472
+ }
473
+
474
+ public function notifications_list( $echo = true ) {
475
+ $items = [];
476
+ $list_html = '';
477
+ if( ! empty( $this->notifications ) && is_array( $this->notifications ) ) {
478
+ $list_html .= '<ul>';
479
+ foreach( $this->notifications as $n ) {
480
+ if( $type = wpfval( $n, 'type' ) ) {
481
+ $html = wpfval( $this->actions, $type ) ? $this->actions[ $type ] : $this->actions['default'];
482
+ $items[ $n['id'] ] = $html['before'];
483
+ if( wpfval( $n, 'itemid_second' ) ) {
484
+ $member = wpforo_member( $n['itemid_second'] );
485
+ $member_name = wpfval( $member, 'display_name' ) ? $member['display_name'] : wpforo_phrase( 'Guest', false );
486
+ } else {
487
+ $member_name = wpfval( $n, 'name' ) ? $n['name'] : wpforo_phrase( 'Guest', false );
488
+ }
489
+ if( strpos( $n['permalink'], '#' ) === false ) {
490
+ $n['permalink'] = wp_nonce_url( $n['permalink'] . '?_nread=' . $n['id'], 'wpforo_mark_notification_read', 'foro_n' );
491
+ } else {
492
+ $n['permalink'] = str_replace( '#', '?_nread=' . $n['id'] . '#', $n['permalink'] );
493
+ $n['permalink'] = wp_nonce_url( $n['permalink'], 'wpforo_mark_notification_read', 'foro_n' );
494
+ }
495
+ $date = wpforo_date( $n['date'], 'ago', false );
496
+ $length = apply_filters( 'wpforo_notification_description_length', 40 );
497
+ $items[ $n['id'] ] .= '<div class="wpf-nleft">' . $html['icon'] . '</div>';
498
+ $items[ $n['id'] ] .= '<div class="wpf-nright">';
499
+ $items[ $n['id'] ] .= '<a href="' . esc_url_raw( $n['permalink'] ) . '">';
500
+ $items[ $n['id'] ] .= sprintf( $html['description'], '<strong>' . $member_name . '</strong>', $date );
501
+ $items[ $n['id'] ] .= '</a>';
502
+ $items[ $n['id'] ] .= '<div class="wpf-ndesc">' . stripslashes( wpforo_text( $n['content'], $length, false ) ) . '</div>';
503
+ $items[ $n['id'] ] .= '</div>';
504
+ $items[ $n['id'] ] .= $html['after'];
505
+ }
506
+ }
507
+ $items = apply_filters( 'wpforo_notifications_list', $items );
508
+ $list_html .= implode( "\r\n", $items );
509
+ $list_html .= '</ul>';
510
+ } else {
511
+ $list_html = $this->get_no_notifications_html();
512
+ }
513
+ if( $echo ) echo $list_html;
514
+
515
+ return $list_html;
516
+ }
517
+
518
+ public function get_no_notifications_html() {
519
+ return '<div class="wpf-no-notification">' . wpforo_phrase( 'You have no new notifications', false ) . '</div>';
520
+ }
521
+
522
+ public function get_notifications() {
523
+ $args = [ 'itemtype' => 'alert', 'userid' => WPF()->current_userid, 'row_count' => 100 ];
524
+ $args = apply_filters( 'wpforo_get_notifications_args', $args );
525
+
526
+ return $this->get_activities( $args );
527
+ }
528
+
529
+ public function add_notification_new_reply( $type, $post, $topic = [] ) {
530
+ if( ! wpfval( $post, 'status' ) ) {
531
+ $replied_post = wpforo_post( $post['parentid'] );
532
+ // Notify replied person
533
+ if( ! empty( $replied_post ) && wpfval( $replied_post, 'userid' ) != wpfval( $post, 'userid' ) ) {
534
+ $notification = [
535
+ 'type' => $type,
536
+ 'itemid' => $post['postid'],
537
+ 'itemtype' => 'alert',
538
+ 'itemid_second' => $post['userid'],
539
+ 'userid' => $replied_post['userid'],
540
+ 'name' => $post['name'],
541
+ 'email' => $post['email'],
542
+ 'content' => $post['title'],
543
+ 'permalink' => $post['posturl'],
544
+ 'new' => 1,
545
+ ];
546
+ $this->add( $notification );
547
+ }
548
+ // Notify the topic author
549
+ if( ! empty( $topic ) && $topic['userid'] != $post['userid'] && ! ( ! empty( $replied_post ) && $topic['userid'] == $replied_post['userid'] ) ) {
550
+ $notification = [
551
+ 'type' => $type,
552
+ 'itemid' => $post['postid'],
553
+ 'itemtype' => 'alert',
554
+ 'itemid_second' => $post['userid'],
555
+ 'userid' => $topic['userid'],
556
+ 'name' => $post['name'],
557
+ 'email' => $post['email'],
558
+ 'content' => $post['title'],
559
+ 'permalink' => $post['posturl'],
560
+ 'new' => 1,
561
+ ];
562
+ $this->add( $notification );
563
+ }
564
+ }
565
+ }
566
+
567
+ public function add_notification( $type, $args ) {
568
+ if( $args['userid'] != WPF()->current_userid ) {
569
+ $length = apply_filters( 'wpforo_notification_saved_description_length', 50 );
570
+ $notification = [
571
+ 'type' => $type,
572
+ 'itemid' => $args['itemid'],
573
+ 'itemtype' => 'alert',
574
+ 'itemid_second' => WPF()->current_userid,
575
+ 'userid' => $args['userid'],
576
+ 'name' => WPF()->current_user_display_name,
577
+ 'email' => WPF()->current_user_email,
578
+ 'content' => wpforo_text( $args['content'], $length, false ),
579
+ 'permalink' => ( wpfval( $args, 'permalink' ) ? $args['permalink'] : '#' ),
580
+ 'new' => 1,
581
+ ];
582
+ $this->add( $notification );
583
+ }
584
+ }
585
+
586
+ public function after_like( $post ) {
587
+ if( $post ) {
588
+ $args = [
589
+ 'itemid' => $post['postid'],
590
+ 'userid' => $post['userid'],
591
+ 'content' => $post['body'],
592
+ 'permalink' => WPF()->post->get_post_url( $post['postid'] ),
593
+ ];
594
+ $this->add_notification( 'new_like', $args );
595
+ }
596
+ }
597
+
598
+ public function after_dislike( $post ) {
599
+ $args = [
600
+ 'type' => 'new_like',
601
+ 'itemid' => $post['postid'],
602
+ 'itemtype' => 'alert',
603
+ 'itemid_second' => WPF()->current_userid,
604
+ ];
605
+ $this->delete_notification( $args );
606
+ }
607
+
608
+ public function after_vote( $reaction, $post ) {
609
+ if( $post ) {
610
+ if( $reaction == 1 ) {
611
+ $args = [
612
+ 'itemid' => $post['postid'],
613
+ 'userid' => $post['userid'],
614
+ 'content' => $post['body'],
615
+ 'permalink' => WPF()->post->get_post_url( $post['postid'] ),
616
+ ];
617
+ $this->add_notification( 'new_up_vote', $args );
618
+ $args = [
619
+ 'type' => 'new_down_vote',
620
+ 'itemid' => $post['postid'],
621
+ 'itemtype' => 'alert',
622
+ 'itemid_second' => WPF()->current_userid,
623
+ ];
624
+ $this->delete_notification( $args );
625
+ } elseif( $reaction == - 1 ) {
626
+ $args = [
627
+ 'itemid' => $post['postid'],
628
+ 'userid' => $post['userid'],
629
+ 'content' => $post['body'],
630
+ 'permalink' => WPF()->post->get_post_url( $post['postid'] ),
631
+ ];
632
+ $this->add_notification( 'new_down_vote', $args );
633
+ $args = [
634
+ 'type' => 'new_up_vote',
635
+ 'itemid' => $post['postid'],
636
+ 'itemtype' => 'alert',
637
+ 'itemid_second' => WPF()->current_userid,
638
+ ];
639
+ $this->delete_notification( $args );
640
+ }
641
+ }
642
+ }
643
+
644
+ public function delete_notification( $args ) {
645
+ $this->delete( $args );
646
+ }
647
+
648
+ public function update_notification( $post, $status ) {
649
+ $post['status'] = $status = intval($status);
650
+ $post['posturl'] = WPF()->post->get_post_url( $post['postid'] );
651
+ if( wpfval( $post, 'topicid' ) ) {
652
+ $topic = WPF()->topic->get_topic( $post['topicid'] );
653
+ if( $status ) {
654
+ $args = [
655
+ 'type' => 'new_reply',
656
+ 'itemid' => $post['postid'],
657
+ 'itemtype' => 'alert',
658
+ ];
659
+ $this->delete_notification( $args );
660
+ } else {
661
+ $this->add_notification_new_reply( 'new_reply', $post, $topic );
662
+ }
663
+ }
664
+ }
665
+
666
+ public function read_notification( $id, $userid = null ) {
667
+ $userid = is_null( $userid ) ? WPF()->current_userid : $userid;
668
+ $args = [
669
+ 'id' => $id,
670
+ 'userid' => $userid,
671
+ ];
672
+ $this->delete_notification( $args );
673
+ }
674
+
675
+ public function clear_notifications( $userid = null ) {
676
+ $userid = is_null( $userid ) ? WPF()->current_userid : $userid;
677
+ $args = [
678
+ 'userid' => $userid,
679
+ ];
680
+ $this->delete_notification( $args );
681
+ }
682
+
683
+ }
classes/Boards.php ADDED
@@ -0,0 +1,489 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ use stdClass;
6
+ use wpforo\admin\listtables\Boards as BoardsListTable;
7
+
8
+ // Exit if accessed directly
9
+ if( ! defined( 'ABSPATH' ) ) exit;
10
+
11
+ class Boards {
12
+ public $default;
13
+ private $board;
14
+ public $list_table;
15
+ public $route = 'community';
16
+ public $full_route = 'community';
17
+ public $base_routes = [];
18
+ public $routes = [];
19
+ public $is_inited = [];
20
+
21
+ public function __construct() {
22
+ $this->init_defaults();
23
+ $this->reset_current();
24
+ $this->init_routes();
25
+ $this->init_hooks();
26
+ if( is_admin() ) add_action( 'wpforo_after_init', [ $this, 'init_list_table' ] );
27
+ }
28
+
29
+ public function init_hooks() {
30
+ add_action('wpforo_after_add_board', function( $board ) {
31
+ $this->after_save_board( $board['boardid'] );
32
+ });
33
+ add_action('wpforo_after_edit_board', function( $boardid ) {
34
+ $this->after_save_board( $boardid );
35
+ });
36
+ add_filter('wpforo_settings_init_core_info', function( $settings_info ){
37
+ return array_filter($settings_info, function( $v, $k ){
38
+ return $v['base'] || $this->is_module_enabled( $k );
39
+ }, ARRAY_FILTER_USE_BOTH );
40
+ });
41
+ add_filter('wpforo_settings_init_addons_info', function( $settings_info ){
42
+ return array_filter($settings_info, function( $v, $k ){
43
+ return $v['base'] || $this->is_module_enabled( $k );
44
+ }, ARRAY_FILTER_USE_BOTH );
45
+ });
46
+ }
47
+
48
+ public function init_list_table() {
49
+ if( wpfval( $_GET, 'page' ) === 'wpforo-boards' ) {
50
+ $this->list_table = new BoardsListTable();
51
+ $this->list_table->prepare_items();
52
+ }
53
+ }
54
+
55
+ private function init_defaults() {
56
+ $this->default = new stdClass();
57
+ $this->default->board = [
58
+ 'boardid' => 0,
59
+ 'title' => 'Forums',
60
+ 'slug' => 'community',
61
+ 'pageid' => 0,
62
+ 'modules' => [],
63
+ 'locale' => '',
64
+ 'is_standalone' => false,
65
+ 'excld_urls' => [],
66
+ 'status' => true,
67
+ 'settings' => [
68
+ 'title' => 'Forum',
69
+ 'desc' => 'Discussion Board',
70
+ ],
71
+ ];
72
+ $this->default->board_format = [
73
+ 'boardid' => '%d',
74
+ 'title' => '%s',
75
+ 'slug' => '%s',
76
+ 'pageid' => '%d',
77
+ 'modules' => '%s',
78
+ 'locale' => '%s',
79
+ 'is_standalone' => '%d',
80
+ 'excld_urls' => '%s',
81
+ 'status' => '%d',
82
+ 'settings' => '%s',
83
+ ];
84
+ $this->default->sql_select_args = [
85
+ 'title_like' => null,
86
+ 'title_notlike' => null,
87
+ 'locale_like' => null,
88
+ 'locale_notlike' => null,
89
+ 'locale_empty' => null,
90
+ 'is_standalone' => null,
91
+ 'status' => null,
92
+ 'slug_include' => [],
93
+ 'slug_exclude' => [],
94
+ 'boardid_include' => [],
95
+ 'boardid_exclude' => [],
96
+ 'pageid_include' => [],
97
+ 'pageid_exclude' => [],
98
+ 'orderby' => null,
99
+ 'offset' => null,
100
+ 'row_count' => null,
101
+ ];
102
+ }
103
+
104
+ /**
105
+ * @param $board
106
+ *
107
+ * @return array
108
+ */
109
+ public function decode( $board ) {
110
+ $board = array_merge( $this->default->board, (array) $board );
111
+ $board['boardid'] = intval( $board['boardid'] );
112
+ $board['pageid'] = wpforo_bigintval( $board['pageid'] );
113
+ $board['title'] = trim( strip_tags( $board['title'] ) );
114
+ $board['is_standalone'] = (bool) intval( $board['is_standalone'] );
115
+ $board['status'] = (bool) intval( $board['status'] );
116
+ $board['slug'] = sanitize_title( $board['slug'], 'community' );
117
+ $board['locale'] = trim( strip_tags( $board['locale'] ) );
118
+ $board['excld_urls'] = array_values(
119
+ array_unique(
120
+ array_filter(
121
+ (array) ( wpforo_is_json( $board['excld_urls'] ) ? json_decode( $board['excld_urls'], true ) : ( is_scalar( $board['excld_urls'] ) ? array_map( 'trim', wpforo_string2array( sanitize_textarea_field( $board['excld_urls'] ) ) ) : $board['excld_urls'] ) )
122
+ )
123
+ )
124
+ );
125
+
126
+ // -- START -- decode board settings
127
+ $board['settings'] = (array) ( wpforo_is_json( $board['settings'] ) ? json_decode( $board['settings'], true ) : ( is_scalar( $board['settings'] ) ? array_map( 'trim', wpforo_string2array( sanitize_textarea_field( $board['settings'] ) ) ) : $board['settings'] ) );
128
+ $board['settings'] = wpforo_array_args_cast_and_merge( $board['settings'], $this->default->board['settings'] );
129
+ $board['settings']['title'] = trim( strip_tags( $board['settings']['title'] ) );
130
+ $board['settings']['desc'] = trim( strip_tags( $board['settings']['desc'] ) );
131
+ // -- END -- decode board settings
132
+
133
+ if( ! $board['locale'] ) $board['locale'] = wpforo_get_site_default_locale();
134
+ if( ! $board['pageid'] ) $board['pageid'] = wpforo_get_option( 'wpforo_pageid', 0 );
135
+
136
+ $all_modules = array_map( '__return_true', wpforo_get_modules_info() );
137
+ $all_addons = array_map( '__return_true', wpforo_get_addons_info() );
138
+ $modules = (array) ( ( wpforo_is_json( $board['modules'] ) ) ? json_decode( $board['modules'], true ) : $board['modules'] );
139
+ $modules = array_merge( $all_modules, $all_addons, $modules );
140
+ $board['modules'] = array_map( function( $a ) { return (bool) intval( $a ); }, $modules );
141
+
142
+ return $board;
143
+ }
144
+
145
+ /**
146
+ * @param $board
147
+ *
148
+ * @return array
149
+ */
150
+ private function encode( $board ) {
151
+ $board = $this->decode( $board );
152
+ $board['modules'] = json_encode( $board['modules'] );
153
+ $board['excld_urls'] = json_encode( $board['excld_urls'] );
154
+ $board['settings'] = json_encode( $board['settings'] );
155
+ $board['is_standalone'] = intval( $board['is_standalone'] );
156
+ $board['status'] = intval( $board['status'] );
157
+
158
+ return $board;
159
+ }
160
+
161
+ /**
162
+ * @param $board
163
+ *
164
+ * @return false|int
165
+ */
166
+ public function add( $board ) {
167
+ $board = $this->encode( $board );
168
+ unset( $board['boardid'] );
169
+ $board = wpforo_array_ordered_intersect_key( $board, $this->default->board_format );
170
+ if( WPF()->db->insert(
171
+ WPF()->tables->boards,
172
+ $board,
173
+ wpforo_array_ordered_intersect_key( $this->default->board_format, $board )
174
+ ) ) {
175
+ flush_rewrite_rules( false );
176
+ nocache_headers();
177
+
178
+ $board['boardid'] = WPF()->db->insert_id;
179
+ do_action( 'wpforo_after_add_board', $board );
180
+
181
+ return $board['boardid'];
182
+ }
183
+
184
+ return false;
185
+ }
186
+
187
+ /**
188
+ * @param $fields
189
+ * @param $boardid
190
+ *
191
+ * @return bool
192
+ */
193
+ public function edit( $fields, $boardid ) {
194
+ $boardid = intval( $boardid );
195
+ $fields = wpforo_array_ordered_intersect_key( $fields, $this->default->board_format );
196
+ if( false !== WPF()->db->update( WPF()->tables->boards, $data = wpforo_array_ordered_intersect_key( $this->encode( $fields ), $fields ), [ 'boardid' => $boardid ], wpforo_array_ordered_intersect_key( $this->default->board_format, $fields ), [ '%d' ] ) ) {
197
+ flush_rewrite_rules( false );
198
+ nocache_headers();
199
+
200
+ do_action( 'wpforo_after_edit_board', $boardid, $data );
201
+
202
+ return true;
203
+ }
204
+
205
+ return false;
206
+ }
207
+
208
+ /**
209
+ * @param $boardid
210
+ *
211
+ * @return bool
212
+ */
213
+ public function delete( $boardid ) {
214
+ do_action( 'wpforo_before_delete_board', $boardid );
215
+
216
+ if( false !== WPF()->db->delete( WPF()->tables->boards, [ 'boardid' => $boardid ], [ '%d' ] ) ) {
217
+ flush_rewrite_rules( false );
218
+ nocache_headers();
219
+
220
+ do_action( 'wpforo_after_delete_board', $boardid );
221
+
222
+ return true;
223
+ }
224
+
225
+ return false;
226
+ }
227
+
228
+ private function after_save_board( $boardid ) {
229
+ if( $board = $this->_get_board( (int) $boardid ) ) {
230
+ if( $board['status'] && $board['is_standalone'] ) {
231
+ $sql = "UPDATE `" . WPF()->tables->boards . "` SET `is_standalone` = 0 WHERE `is_standalone` = 1 AND `boardid` <> %d";
232
+ WPF()->db->query( WPF()->db->prepare( $sql, $board['boardid'] ) );
233
+ }
234
+ }
235
+ }
236
+
237
+ private function parse_args( $args ) {
238
+ $args = wpforo_parse_args( $args, $this->default->sql_select_args );
239
+ $args = wpforo_array_ordered_intersect_key( $args, $this->default->sql_select_args );
240
+ $args['slug_include'] = wpforo_parse_args( $args['slug_include'] );
241
+ $args['slug_exclude'] = wpforo_parse_args( $args['slug_exclude'] );
242
+ $args['boardid_include'] = wpforo_parse_args( $args['boardid_include'] );
243
+ $args['boardid_exclude'] = wpforo_parse_args( $args['boardid_exclude'] );
244
+ $args['pageid_include'] = wpforo_parse_args( $args['pageid_include'] );
245
+ $args['pageid_exclude'] = wpforo_parse_args( $args['pageid_exclude'] );
246
+
247
+ return $args;
248
+ }
249
+
250
+ private function build_sql_select( $args, $select = '' ) {
251
+ $args = $this->parse_args( $args );
252
+ if( ! $select ) $select = '*';
253
+
254
+ $wheres = [];
255
+
256
+ if( ! is_null( $args['title_like'] ) ) $wheres[] = "`title` LIKE '%" . WPF()->db->esc_like( $args['title_like'] ) . "%'";
257
+ if( ! is_null( $args['title_notlike'] ) ) $wheres[] = "`title` NOT LIKE '%" . WPF()->db->esc_like( $args['title_notlike'] ) . "%'";
258
+
259
+ if( ! is_null( $args['locale_like'] ) ) {
260
+ $locale_like = "`locale` LIKE '%" . WPF()->db->esc_like( $args['locale_like'] ) . "%'";
261
+ if( $args['locale_empty'] ) {
262
+ $locale_like = "( `locale` = '' OR `locale` IS NULL OR " . $locale_like . " )";
263
+ }
264
+ $wheres[] = $locale_like;
265
+ }
266
+ if( ! is_null( $args['locale_notlike'] ) ) {
267
+ $locale_notlike = "`locale` NOT LIKE '%" . WPF()->db->esc_like( $args['locale_notlike'] ) . "%'";
268
+ if( ! is_null( $args['locale_empty'] ) ) {
269
+ if( $args['locale_empty'] ) {
270
+ $locale_notlike = "( `locale` = '' OR `locale` IS NULL OR " . $locale_notlike . " )";
271
+ } else {
272
+ $locale_notlike = "( `locale` <> '' AND `locale` IS NOT NULL AND " . $locale_notlike . " )";
273
+ }
274
+ }
275
+ $wheres[] = $locale_notlike;
276
+ }
277
+ if( ! is_null( $args['locale_empty'] ) && is_null( $args['locale_like'] ) && is_null( $args['locale_notlike'] ) ) {
278
+ if( $args['locale_empty'] ) {
279
+ $wheres[] = "( `locale` = '' OR `locale` IS NULL )";
280
+ } else {
281
+ $wheres[] = "( `locale` <> '' AND `locale` IS NOT NULL )";
282
+ }
283
+ }
284
+
285
+ if( ! is_null( $args['status'] ) ) $wheres[] = "`status` = '" . intval( $args['status'] ) . "'";
286
+ if( ! is_null( $args['is_standalone'] ) ) $wheres[] = "`is_standalone` = '" . intval( $args['is_standalone'] ) . "'";
287
+
288
+ if( ! empty( $args['slug_include'] ) ) $wheres[] = "`slug` IN('" . implode( "','", array_map( 'trim', $args['slug_include'] ) ) . "')";
289
+ if( ! empty( $args['slug_exclude'] ) ) $wheres[] = "`slug` NOT IN(" . implode( "','", array_map( 'trim', $args['slug_exclude'] ) ) . "')";
290
+
291
+ if( ! empty( $args['boardid_include'] ) ) $wheres[] = "`boardid` IN(" . implode( ',', array_map( 'intval', $args['boardid_include'] ) ) . ")";
292
+ if( ! empty( $args['boardid_exclude'] ) ) $wheres[] = "`boardid` NOT IN(" . implode( ',', array_map( 'intval', $args['boardid_exclude'] ) ) . ")";
293
+
294
+ if( ! empty( $args['pageid_include'] ) ) $wheres[] = "`pageid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['pageid_include'] ) ) . ")";
295
+ if( ! empty( $args['pageid_exclude'] ) ) $wheres[] = "`pageid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['pageid_exclude'] ) ) . ")";
296
+
297
+ $wheres = array_filter( $wheres );
298
+
299
+ $sql = "SELECT $select FROM " . WPF()->tables->boards;
300
+ if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
301
+ if( $args['orderby'] ) $sql .= " ORDER BY " . $args['orderby'];
302
+ if( $args['row_count'] ) $sql .= " LIMIT " . intval( $args['offset'] ) . "," . intval( $args['row_count'] );
303
+
304
+ return $sql;
305
+ }
306
+
307
+ /**
308
+ * @param array|numeric $args
309
+ *
310
+ * @return array
311
+ */
312
+ public function _get_board( $args ) {
313
+ if( is_numeric( $args ) ) $args = [ 'boardid_include' => (int) $args ];
314
+ if( ! wpfkey( $args, 'orderby' ) ) $args['orderby'] = '`boardid` DESC';
315
+ if( ! wpfkey( $args, 'row_count' ) ) {
316
+ $args['offset'] = 0;
317
+ $args['row_count'] = 1;
318
+ }
319
+ $board = (array) WPF()->db->get_row( $this->build_sql_select( $args ), ARRAY_A );
320
+ if( $board ) $board = $this->decode( $board );
321
+
322
+ return $board;
323
+ }
324
+
325
+ /**
326
+ * @param array|numeric $args
327
+ *
328
+ * @return array
329
+ */
330
+ public function get_board( $args ) {
331
+ return wpforo_ram_get( [$this, '_get_board'], $args );
332
+ }
333
+
334
+ /**
335
+ * @param array $args
336
+ *
337
+ * @return array
338
+ */
339
+ public function _get_boards( $args = [] ) {
340
+ return array_map( [ $this, 'decode' ], (array) WPF()->db->get_results( $this->build_sql_select( $args ), ARRAY_A ) );
341
+ }
342
+
343
+ public function get_boards( $args = [] ) {
344
+ return wpforo_ram_get( [ $this, '_get_boards' ], $args );
345
+ }
346
+
347
+ /**
348
+ * @param array $args
349
+ *
350
+ * @return int
351
+ */
352
+ public function get_count( $args = [] ) {
353
+ return (int) WPF()->db->get_var( $this->build_sql_select( $args, 'COUNT(*)' ) );
354
+ }
355
+
356
+ public function reset_current() {
357
+ $this->board = $this->default->board;
358
+ }
359
+
360
+ private function init_routes() {
361
+ $this->base_routes = array_unique( array_merge( $this->base_routes, array_keys( WPF()->tpl->base_templates ) ) );
362
+ $this->base_routes = array_map( 'wpforo_settings_get_slug', $this->base_routes );
363
+ $slugs = [];
364
+ if( WPF()->is_installed() ){
365
+ $boards = $this->get_boards( [ 'status' => true ] );
366
+ foreach( $boards as $board ) $slugs[] = $board['slug'];
367
+ }
368
+ $this->routes = array_unique( array_merge( $this->base_routes, $slugs ) );
369
+ if( ! $this->routes ) $this->routes = (array) $this->route;
370
+ $this->routes = preg_replace( '#^/?index\.php/?#isu', '', $this->routes );
371
+ $this->routes = array_map( function( $route ) { return trim( $route, '/' ); }, $this->routes );
372
+ }
373
+
374
+ /**
375
+ * @param array $args
376
+ */
377
+ public function init_current( $args = [] ) {
378
+ if( !WPF()->is_installed() ) return;
379
+ $get = wp_unslash( array_merge( WPF()->GET, $_GET ) );
380
+ if( is_numeric( $args ) ) $args = ['boardid' => intval( $args )];
381
+ $board = [];
382
+ if( is_null( $boardid = wpfval( $args, 'boardid' ) ) && is_null( $boardid = wpfval( $get, 'boardid' ) ) && is_null( $boardid = wpfval( $get, 'wpforo_boardid' ) ) ) {
383
+ if( wpforo_is_admin() ) {
384
+ if( preg_match( '#^wpforo-(?:(\d*)-)?#iu', wpfval( $get, 'page' ), $match ) ) {
385
+ if( ! $boardid = (int) wpfval( $get, 'boardid' ) ) {
386
+ $boardid = (int) wpfval( $match, 1 );
387
+ }
388
+ }
389
+ } else {
390
+ if( $slug = (string) wpfval( $args, 'slug' ) ) {
391
+ $args = [
392
+ 'slug_include' => $slug,
393
+ 'locale_like' => ( $locale = (string) wpfval( $args, 'locale' ) ) ? $locale : wpforo_get_site_default_locale(),
394
+ 'locale_empty' => true,
395
+ 'status' => true,
396
+ 'orderby' => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
397
+ ];
398
+ $board = $this->get_board( $args );
399
+ }
400
+ if( ! $board && $pageid = get_the_ID() ) {
401
+ $args = [
402
+ 'pageid_include' => $pageid,
403
+ 'status' => true,
404
+ 'orderby' => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
405
+ ];
406
+ if( $slug = wpforo_get_url_route() ) $args['slug_include'] = $slug;
407
+ $board = $this->get_board( $args );
408
+ }
409
+ if( ! $board && $slug = wpforo_get_url_route() ) {
410
+ $args = [
411
+ 'slug_include' => $slug,
412
+ 'locale_like' => ( $locale = (string) wpfval( $args, 'locale' ) ) ? $locale : wpforo_get_site_default_locale(),
413
+ 'locale_empty' => true,
414
+ 'status' => true,
415
+ 'orderby' => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
416
+ ];
417
+ $board = $this->get_board( $args );
418
+ }
419
+ if( ! $board ) {
420
+ $args = [
421
+ 'is_standalone' => true,
422
+ 'status' => true,
423
+ 'orderby' => "( `locale` = '' OR `locale` IS NULL ) ASC, `boardid` DESC",
424
+ ];
425
+ $board = $this->get_board( $args );
426
+ }
427
+ }
428
+ }
429
+
430
+ if( $board || ( $board = $this->get_board( intval( $boardid ) ) ) ) {
431
+ $this->board = $board;
432
+ } else {
433
+ $this->board = $this->default->board;
434
+ }
435
+
436
+ $this->board = apply_filters('wpforo_board_init_current', $this->board);
437
+
438
+ if( ! $this->board['pageid'] ) $this->board['pageid'] = wpforo_get_option( 'wpforo_pageid', 0 );
439
+ if( ! wpforo_is_admin() && $this->board['locale'] && WPF()->locale !== $this->board['locale'] ) {
440
+ if( is_wpforo_url() && is_wpforo_multiboard() ) switch_to_locale( $this->board['locale'] );
441
+ }
442
+
443
+ $this->route = ( ! $this->board['is_standalone'] ? $this->board['slug'] : '' );
444
+ $this->full_route = rtrim(
445
+ ( strpos( get_option( 'permalink_structure', '' ), 'index.php' ) !== false ? '/index.php/' : '/' ) . $this->route,
446
+ '/\\'
447
+ );
448
+ ## @TODO need to test with or without lang plugins
449
+ // if( $lang = wpforo_get_query_var_lang() ) $this->full_route = "/$lang" . $this->full_route;
450
+
451
+ load_plugin_textdomain( 'wpforo', false, basename( WPFORO_DIR ) . '/languages' );
452
+
453
+ $this->is_inited[ $this->board['boardid'] ] = true;
454
+ }
455
+
456
+ /**
457
+ * @param string $field
458
+ *
459
+ * @return mixed
460
+ */
461
+ public function get_current( $field = null ) {
462
+ if( is_null( $field ) ) return $this->board;
463
+
464
+ return wpfval( $this->board, $field );
465
+ }
466
+
467
+ /**
468
+ * @param string $module_key
469
+ * @param array $board
470
+ *
471
+ * @return bool
472
+ */
473
+ public function is_module_enabled( $module_key, $board = [] ) {
474
+ if( ! ( $board = (array) $board ) ) $board = $this->get_current();
475
+
476
+ return !wpfkey( $board, 'modules', $module_key ) || wpfval( $board, 'modules', $module_key );
477
+ }
478
+
479
+ public function is_multi() {
480
+ return 1 < $this->get_count( [ 'status' => true ] );
481
+ }
482
+
483
+ public function get_active_boardids() {
484
+ $boardids = [];
485
+ $boards = $this->get_boards( [ 'status' => true ] );
486
+ foreach( $boards as $board ) $boardids[] = $board['boardid'];
487
+ return $boardids;
488
+ }
489
+ }
classes/Cache.php ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ use FilesystemIterator;
6
+
7
+ // Exit if accessed directly
8
+ if( ! defined( 'ABSPATH' ) ) exit;
9
+
10
+ class Cache {
11
+ public $object;
12
+ public $dir;
13
+ public $lang;
14
+
15
+ function __construct() {
16
+ add_action( 'wpforo_after_init_folders', function( $folders ) {
17
+ $cache_dir = $folders['cache']['dir'];
18
+ if( ! is_dir( $cache_dir ) ) $this->dir( $cache_dir );
19
+ if( ! is_dir( $cache_dir . DIRECTORY_SEPARATOR . 'tag' ) ) $this->mkdir( $cache_dir . DIRECTORY_SEPARATOR . 'tag' );
20
+ if( ! is_dir( $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'tag' ) ) $this->mkdir( $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'tag' );
21
+ if( ! is_dir( $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'option' ) ) $this->mkdir( $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'option' );
22
+
23
+ $this->dir = $cache_dir;
24
+ } );
25
+
26
+ add_action( 'wpforo_after_set_locale', function( $locale ) { $this->lang = $locale; } );
27
+
28
+ add_action( 'wpforo_after_add_board', function( $board ){
29
+ if( $board['status'] ){
30
+ WPF()->change_board( $board['boardid'] );
31
+ wpforo_clean_cache();
32
+ }
33
+ } );
34
+
35
+ add_action( 'wpforo_after_edit_board', function( $boardid ){
36
+ $board = WPF()->board->_get_board( $boardid );
37
+ if( wpfval($board, 'status' ) ){
38
+ WPF()->change_board( $boardid );
39
+ wpforo_clean_cache();
40
+ }
41
+ } );
42
+ }
43
+
44
+ public function get_key( $type = 'html' ) {
45
+ if( $type === 'html' ) {
46
+ $ug = WPF()->current_user_groupid;
47
+
48
+ return md5( preg_replace( '#(.+)\#.+?$#is', '$1', $_SERVER['REQUEST_URI'] ) . $ug );
49
+ }
50
+ }
51
+
52
+ private function dir( $cache_dir ) {
53
+ $dirs = [
54
+ $cache_dir,
55
+ $cache_dir . DIRECTORY_SEPARATOR . 'forum',
56
+ $cache_dir . DIRECTORY_SEPARATOR . 'topic',
57
+ $cache_dir . DIRECTORY_SEPARATOR . 'post',
58
+ $cache_dir . DIRECTORY_SEPARATOR . 'tag',
59
+ $cache_dir . DIRECTORY_SEPARATOR . 'item',
60
+ $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'forum',
61
+ $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'topic',
62
+ $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'post',
63
+ $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'tag',
64
+ $cache_dir . DIRECTORY_SEPARATOR . 'item' . DIRECTORY_SEPARATOR . 'option',
65
+ ];
66
+ $this->mkdir( $dirs );
67
+ }
68
+
69
+ private function mkdir( $dirs ) {
70
+ foreach( (array) $dirs as $dir ) {
71
+ wp_mkdir_p( $dir );
72
+ wpforo_write_file( $dir . '/index.html', '' );
73
+ wpforo_write_file( $dir . '/.htaccess', 'deny from all' );
74
+ }
75
+ }
76
+
77
+ public function on(){
78
+ return wpforo_setting( 'board', 'cache' );
79
+ }
80
+
81
+ public function get( $key, $type = 'loop', $template = null ) {
82
+ $template = ( $template ) ?: WPF()->current_object['template'];
83
+ $loop_templates = [ 'forum', 'topic', 'post', 'tag', 'option' ];
84
+ if( $type === 'loop' && $template ) {
85
+ if( $this->exists( $key, $template ) ) {
86
+ if( in_array( $template, $loop_templates ) ) {
87
+ $cache_file = $this->dir . '/' . $template . '/' . $key;
88
+ $array = wpforo_get_file_content( $cache_file );
89
+
90
+ return @unserialize( $array );
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ public function get_item( $id, $type = 'post' ) {
97
+ if( $id ) {
98
+ $key = $id . '_' . $this->lang;
99
+ if( $this->exists( $key, 'item', $type ) ) {
100
+ $cache_file = $this->dir . '/item/' . $type . '/' . $key;
101
+ $array = wpforo_get_file_content( $cache_file );
102
+
103
+ return @unserialize( $array );
104
+ }
105
+ }
106
+ }
107
+
108
+ public function get_html() {
109
+ $template = WPF()->current_object['template'];
110
+ if( $template == 'forum' ) {
111
+ $key = $this->get_key();
112
+ if( $this->exists( $key, $template ) ) {
113
+ $cache_file = $this->dir . '/' . $template . '/' . $key;
114
+ $html = wpforo_get_file_content( $cache_file );
115
+
116
+ return $this->filter( $html );
117
+ }
118
+ }
119
+
120
+ return false;
121
+ }
122
+
123
+ public function html( $content ) {
124
+ /*$template = WPF()->current_object['template'];
125
+ if( $template === 'forum' ){
126
+ $key = $this->get_key();
127
+ $this->create_html( $content, $template, $key );
128
+ }*/
129
+ }
130
+
131
+ public function create( $mode = 'loop', $cache = [], $type = 'post' ) {
132
+ if( ! $this->on() ) return false;
133
+ $template = WPF()->current_object['template'];
134
+ if( $template == 'forum' ) {
135
+ $this->check( $this->dir . '/item/post' );
136
+ }
137
+
138
+ if( $mode === 'loop' && $template ) {
139
+ if( wpfval( $cache, 'tags' ) ) {
140
+ $this->create_files( $cache['tags'], 'tag' );
141
+ }
142
+ if( $template === 'forum' || $template === 'topic' || $template === 'post' ) {
143
+ $cache = WPF()->forum->get_cache( 'forums' );
144
+ $this->create_files( $cache, $template );
145
+ $cache = WPF()->topic->get_cache( 'topics' );
146
+ $this->create_files( $cache, $template );
147
+ $cache = WPF()->post->get_cache( 'posts' );
148
+ $this->create_files( $cache, $template );
149
+ }
150
+ } elseif( $mode === 'item' && ! empty( $cache ) ) {
151
+ $this->create_files( $cache, 'item', $type );
152
+ }
153
+ }
154
+
155
+ public function create_files( $cache = [], $template = '', $type = '' ) {
156
+ if( ! empty( $cache ) ) {
157
+ $type = ( $type ) ? $type . '/' : '';
158
+ foreach( $cache as $key => $object ) {
159
+ if( $template == 'item' ) $key = $key . '_' . $this->lang;
160
+ if( ! $this->exists( $key, $template ) ) {
161
+ $object = serialize( $object );
162
+ wpforo_write_file( $this->dir . '/' . $template . '/' . $type . $key, $object );
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+ public function create_html( $content, $template = '', $key = '' ) {
169
+ if( $content ) {
170
+ if( ! $this->exists( $key, $template ) ) {
171
+ wpforo_write_file( $this->dir . '/' . $template . '/' . $key, $content );
172
+ }
173
+ }
174
+ }
175
+
176
+ public function create_custom( $args = [], $items = [], $template = 'post', $items_count = 0 ) {
177
+ if( empty( $args ) || ! is_array( $args ) ) return;
178
+ if( empty( $items ) || ! is_array( $items ) ) return;
179
+ $cache = [];
180
+ $hach = serialize( $args );
181
+ $object_key = md5( $hach . WPF()->current_user_groupid );
182
+ $cache[ $object_key ]['items'] = $items;
183
+ $cache[ $object_key ]['items_count'] = $items_count;
184
+ $this->create_files( $cache, $template );
185
+ }
186
+
187
+ public function filter( $html = '' ) {
188
+ //exit();
189
+ $html = preg_replace( '|<div[\s\t]*id=\"wpf\-msg\-box\"|is', '<div style="display:none;"', $html );
190
+
191
+ return $html;
192
+ }
193
+
194
+ #################################################################################
195
+
196
+ /**
197
+ * Cleans forum cache
198
+ *
199
+ * @param integer Item ID (e.g.: $topicid or $postid) | (!) ID is 0 on dome actions (e.g.: delete actions)
200
+ * @param string Item Type (e.g.: 'forum', 'topic', 'post', 'user', 'widget', etc...)
201
+ * @param array Item data as array
202
+ *
203
+ * @return NULL
204
+ * @since 1.2.1
205
+ *
206
+ */
207
+
208
+ public function clean( $id, $template, $item = [] ) {
209
+
210
+ $dirs = [];
211
+ $userid = ( isset( $item['userid'] ) && $item['userid'] ) ? $item['userid'] : 0;
212
+ $postid = ( isset( $item['postid'] ) && $item['postid'] ) ? $item['postid'] : 0;
213
+ $topicid = ( isset( $item['topicid'] ) && $item['topicid'] ) ? $item['topicid'] : 0;
214
+ $forumid = ( isset( $item['forumid'] ) && $item['forumid'] ) ? $item['forumid'] : 0;
215
+ $parentid = ( isset( $item['parentid'] ) && $item['parentid'] ) ? $item['parentid'] : 0;
216
+ $root = ( isset( $item['root'] ) && $item['root'] ) ? $item['root'] : 0;
217
+ $tagid = ( isset( $item['tagid'] ) && $item['tagid'] ) ? $item['tagid'] : 0;
218
+
219
+ WPF()->forum->reset();
220
+ WPF()->topic->reset();
221
+ WPF()->post->reset();
222
+
223
+ if( $template === 'forum' || $template === 'forum-soft' ) {
224
+ $id = isset( $id ) ? $id : $forumid;
225
+ if( $template === 'forum' ) {
226
+ $dirs = [ $this->dir . '/forum', $this->dir . '/item/forum' ];
227
+ WPF()->seo->clear_cache();
228
+ } elseif( $template === 'forum-soft' ) {
229
+ $dirs = [ $this->dir . '/forum' ];
230
+ }
231
+ if( $id ) {
232
+ $file = $this->dir . '/item/forum/' . $id . '_' . $this->lang;
233
+ $this->clean_file( $file );
234
+ }
235
+ } elseif( $template === 'topic' || $template === 'topic-soft' ) {
236
+ $id = isset( $id ) ? $id : $topicid;
237
+ if( $template === 'topic' ) {
238
+ WPF()->seo->clear_cache();
239
+ $dirs = [ $this->dir . '/forum', $this->dir . '/item/forum', $this->dir . '/topic', $this->dir . '/post' ];
240
+ }
241
+ if( $template === 'topic-soft' && $forumid ) {
242
+ $file = $this->dir . '/item/forum/' . $forumid . '_' . $this->lang;
243
+ $this->clean_file( $file );
244
+ }
245
+ if( $id ) {
246
+ $file = $this->dir . '/item/topic/' . $id . '_' . $this->lang;
247
+ $this->clean_file( $file );
248
+ $postid = ( isset( $item['first_postid'] ) && $item['first_postid'] ) ? $item['first_postid'] : 0;
249
+ if( $postid ) $file = $this->dir . '/item/post/' . $postid . '_' . $this->lang;
250
+ $this->clean_file( $file );
251
+ }
252
+ WPF()->statistic_cache_clean();
253
+ $this->clear_visitor_tracking();
254
+ } elseif( $template === 'post' || $template === 'post-soft' ) {
255
+ $id = isset( $id ) ? $id : $postid;
256
+ if( $template === 'post' ) {
257
+ $dirs = [ $this->dir . '/forum', $this->dir . '/topic', $this->dir . '/post' ];
258
+ }
259
+ if( $forumid ) {
260
+ $file = $this->dir . '/item/forum/' . $forumid . '_' . $this->lang;
261
+ $this->clean_file( $file );
262
+ }
263
+ if( $topicid ) {
264
+ $file = $this->dir . '/item/topic/' . $topicid . '_' . $this->lang;
265
+ $this->clean_file( $file );
266
+ }
267
+ if( $parentid ) {
268
+ $file = $this->dir . '/item/post/' . $parentid . '_' . $this->lang;
269
+ $this->clean_file( $file );
270
+ }
271
+ if( $root ) {
272
+ $file = $this->dir . '/item/post/' . $root . '_' . $this->lang;
273
+ $this->clean_file( $file );
274
+ }
275
+ if( $id ) {
276
+ $file = $this->dir . '/item/post/' . $id . '_' . $this->lang;
277
+ $this->clean_file( $file );
278
+ }
279
+ WPF()->statistic_cache_clean();
280
+ $this->clear_visitor_tracking();
281
+ } elseif( $template === 'tag' ) {
282
+ if( $id ) {
283
+ $file = $this->dir . '/item/tag/' . md5( $id ) . '_' . $this->lang;
284
+ $this->clean_file( $file );
285
+ } else {
286
+ $dirs = [ $this->dir . '/tag' ];
287
+ }
288
+ } elseif( $template === 'option' ) {
289
+ $dirs = [ $this->dir . '/item/option/' ];
290
+ } elseif( $template === 'user' ) {
291
+ if( wpforo_setting( 'seo', 'seo_profile' ) ) WPF()->seo->clear_cache();
292
+ } elseif( $template === 'loop' ) {
293
+ $dirs = [ $this->dir . '/forum', $this->dir . '/topic', $this->dir . '/post' ];
294
+ WPF()->seo->clear_cache();
295
+ } elseif( $template === 'item' ) {
296
+ $dirs = [ $this->dir . '/item/post', $this->dir . '/item/topic', $this->dir . '/item/forum' ];
297
+ WPF()->seo->clear_cache();
298
+ } else {
299
+ $dirs = [ $this->dir . '/forum', $this->dir . '/topic', $this->dir . '/post', $this->dir . '/tag', $this->dir . '/item/post', $this->dir . '/item/topic', $this->dir . '/item/forum', $this->dir . '/item/tag', $this->dir . '/item/option/' ];
300
+ WPF()->seo->clear_cache();
301
+ }
302
+
303
+ if( ! empty( $dirs ) ) {
304
+ foreach( $dirs as $dir ) {
305
+ $this->clean_files( $dir );
306
+ }
307
+ }
308
+
309
+ }
310
+
311
+ public function clean_files( $directory ) {
312
+ $directory = wpforo_fix_dir_sep( $directory );
313
+ $directory_ns = trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
314
+ $directory_ws = DIRECTORY_SEPARATOR . trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
315
+ $glob = glob( $directory_ns );
316
+ if( empty( $glob ) ) $glob = glob( $directory_ws );
317
+ foreach( $glob as $item ) {
318
+ if( strpos( $item, 'index.html' ) !== false || strpos( $item, '.htaccess' ) !== false ) continue;
319
+ if( ! is_dir( $item ) && file_exists( $item ) ) {
320
+ @unlink( $item );
321
+ }
322
+ }
323
+ }
324
+
325
+ public function clean_file( $file ) {
326
+ if( ! is_dir( $file ) && file_exists( $file ) ) {
327
+ @unlink( $file );
328
+ }
329
+ }
330
+
331
+ public function exists( $key, $template, $type = '' ) {
332
+ $type = ( $type ) ? $type . '/' : '';
333
+ if( file_exists( $this->dir . '/' . $template . '/' . $type . $key ) ) {
334
+ return true;
335
+ } else {
336
+ return false;
337
+ }
338
+ }
339
+
340
+ public function check( $directory ) {
341
+ $directory = wpforo_fix_dir_sep( $directory );
342
+ $filecount = 0;
343
+ if( class_exists( 'FilesystemIterator' ) && is_dir( $directory ) ) {
344
+ $fi = new FilesystemIterator( $directory, FilesystemIterator::SKIP_DOTS );
345
+ $filecount = iterator_count( $fi );
346
+ }
347
+ if( ! $filecount ) {
348
+ $directory_ns = trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
349
+ $directory_ws = DIRECTORY_SEPARATOR . trim( $directory, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR . '*';
350
+ $files = glob( $directory_ns );
351
+ if( empty( $files ) ) $files = glob( $directory_ws );
352
+ $filecount = count( $files );
353
+ }
354
+ if( $filecount > 1000 ) {
355
+ $this->clean_files( $directory );
356
+ }
357
+ }
358
+
359
+ public function clear_visitor_tracking() {
360
+ $keep_vistors_data = apply_filters( 'wpforo_keep_visitors_data', 4000 );
361
+ $time = (int) current_time( 'timestamp', 1 ) - (int) $keep_vistors_data;
362
+ $online = (int) current_time( 'timestamp', 1 ) - (int) wpforo_setting( 'profiles', 'online_status_timeout' );
363
+ if( $time > 1 ) {
364
+ WPF()->db->query( "DELETE FROM `" . WPF()->tables->visits . "` WHERE `time` < " . intval( $time ) . " OR (`time` < " . intval( $online ) . " AND `userid` = 0)" );
365
+ }
366
+ }
367
+
368
+ public function cache_plugins(){
369
+ $board_paths = [];
370
+ $cache_plugins = [];
371
+ if( is_wpforo_multiboard() ){
372
+ $boards = WPF()->board->get_boards();
373
+ if(!empty($boards)){
374
+ foreach($boards as $board){
375
+ $board_paths[] = '/' . $board['slug'] . '/';
376
+ }
377
+ }
378
+ } else {
379
+ $board_paths[] = '/' . WPF()->board->get_current( 'slug' ) . '/';
380
+ }
381
+
382
+ if (function_exists("wpsc_init")) {
383
+ //WP Super Cache
384
+ $cache_plugins['WPSuper']['name'] = 'WP Super Cache';
385
+ $cache_plugins['WPSuper']['steps'][] = __('Please navigate in Dashboard to Settings > WP Super Cache', 'wpforo');
386
+ $cache_plugins['WPSuper']['steps'][] = __('Go to Advanced Tab, scroll down to "Rejected URL Strings" option', 'wpforo');
387
+ $cache_plugins['WPSuper']['steps'][] = __('Insert the URL path(s) of your forum page(s) one per line in the option textarea:', 'wpforo') . ' <code>' . implode('</code> ' . __('and', 'wpforo') . ' <code>', $board_paths) . '</code>';
388
+ $cache_plugins['WPSuper']['steps'][] = __('Save it and delete all caches.', 'wpforo');
389
+ }
390
+ if (defined("LSCWP_V")) {
391
+ //LiteSpeed Cache
392
+ $cache_plugins['LiteSpeed']['name'] = 'LiteSpeed Cache';
393
+ $cache_plugins['LiteSpeed']['steps'][] = __('Please navigate in Dashboard to LiteSpeed Cache > Cache admin page', 'wpforo');
394
+ $cache_plugins['LiteSpeed']['steps'][] = __('Go to Exclude Tab, find the "Do Not Cache URIs" option', 'wpforo');
395
+ $cache_plugins['LiteSpeed']['steps'][] = __('Insert the URL path(s) of your forum page(s) one per line in the option textarea:', 'wpforo') . ' <code>' . implode('</code> ' . __('and', 'wpforo') . ' <code>', $board_paths) . '</code>';
396
+ $cache_plugins['LiteSpeed']['steps'][] = __('Save it and delete all caches.', 'wpforo');
397
+ }
398
+ if (function_exists("rocket_clean_post")) {
399
+ //WP Rocket Cache
400
+ $cache_plugins['WPRocket']['name'] = 'WP Rocket Cache';
401
+ $cache_plugins['WPRocket']['steps'][] = __('Please navigate in Dashboard to WP Rocket > Advanced Rules Tab', 'wpforo');
402
+ $cache_plugins['WPRocket']['steps'][] = __('Scroll down to "Never cache (URLs)" option', 'wpforo');
403
+ $cache_plugins['WPRocket']['steps'][] = __('Insert the URL path(s) of your forum page(s) one per line with wildcard (.*) in the option textarea:', 'wpforo') . ' <code>' . implode('</code> ' . __('and', 'wpforo') . ' <code>', array_map( function ( $value ){ return $value . '(.*)'; }, $board_paths)) . '</code>';
404
+ $cache_plugins['WPRocket']['steps'][] = __('Save it and delete all caches.', 'wpforo');
405
+ }
406
+ if (function_exists("wpfc_clear_post_cache_by_id")) {
407
+ //WP Fastest Cache
408
+ $cache_plugins['WPFastest']['name'] = 'WP Fastest Cache';
409
+ $cache_plugins['WPFastest']['steps'][] = __('Please navigate in Dashboard to WP Fastest Cache > Exclude Tab', 'wpforo');
410
+ $cache_plugins['WPFastest']['steps'][] = __('In the "Exclude Pages" section click the [Add New Rule] button', 'wpforo');
411
+ $cache_plugins['WPFastest']['steps'][] = __('Select [Start with] option in the drop-down menu and insert the URL path(s) of your forum page(s) one per rule in the next field:', 'wpforo') . ' <code>' . implode('</code> ' . __('and', 'wpforo') . ' <code>', $board_paths) . '</code>';
412
+ $cache_plugins['WPFastest']['steps'][] = __('If you have more than one forum pages (boards) you should create separate rules for each forum board.', 'wpforo');
413
+ $cache_plugins['WPFastest']['steps'][] = __('Save rules and delete all caches.', 'wpforo');
414
+ }
415
+ if (function_exists("w3tc_flush_post")) {
416
+ //W3 Total Cache
417
+ $cache_plugins['W3Total']['name'] = 'W3 Total Cache';
418
+ $cache_plugins['W3Total']['steps'][] = __('Please navigate in Dashboard to Performance > Page Cache admin page', 'wpforo');
419
+ $cache_plugins['W3Total']['steps'][] = __('Go to Advanced Tab, scroll down to Rejected URL Strings option', 'wpforo');
420
+ $cache_plugins['W3Total']['steps'][] = __('Scroll to Advanced section and insert the URL path(s) of your forum page(s) one per line in the "Never cache the following pages" textarea:', 'wpforo') . ' <code>' . implode('</code> ' . __('and', 'wpforo') . ' <code>', $board_paths) . '</code>';
421
+ $cache_plugins['W3Total']['steps'][] = __('Save it and delete all caches.', 'wpforo');
422
+ }
423
+ if (is_callable(["WPO_Page_Cache", "delete_single_post_cache"])) {
424
+ //WP-Optimize Cache
425
+ $cache_plugins['WPOptimize']['name'] = 'WP-Optimize Cache';
426
+ $cache_plugins['WPOptimize']['steps'][] = __('Please navigate in Dashboard to WP-Optimize > Cache admin page', 'wpforo');
427
+ $cache_plugins['WPOptimize']['steps'][] = __('Go to Advanced Settings Tab, find the "URLs to exclude from caching" option', 'wpforo');
428
+ $cache_plugins['WPOptimize']['steps'][] = __('Insert the URL path(s) of your forum page(s) one per line with wildcard [*] in the option textarea:', 'wpforo') . ' <code>' . implode('</code> ' . __('and', 'wpforo') . ' <code>', array_map( function ( $value ){ return $value . '*'; }, $board_paths)) . '</code>';
429
+ $cache_plugins['WPOptimize']['steps'][] = __('Save it and delete all caches.', 'wpforo');
430
+ }
431
+ if (class_exists("\SiteGround_Optimizer\Supercacher\Supercacher")) {
432
+ //SiteGround Optimizer
433
+ $cache_plugins['SiteGround']['name'] = 'SiteGround Optimizer';
434
+ $cache_plugins['SiteGround']['steps'][] = __('Please navigate in Dashboard to SG Optimizer > Caching admin page', 'wpforo');
435
+ $cache_plugins['SiteGround']['steps'][] = __('Scroll to Exclude URLs from Caching section and click the "pencil" button, enable it, and click the button again', 'wpforo');
436
+ $cache_plugins['SiteGround']['steps'][] = __('Insert the URL path(s) of your forum page(s) with wildcard [*] in the pop-up filed:', 'wpforo') . ' <code>' . implode('</code> ' . __('and', 'wpforo') . ' <code>', array_map( function ( $value ){ return $value . '*'; }, $board_paths)) . '</code>';
437
+ $cache_plugins['SiteGround']['steps'][] = __('Save it and delete all caches.', 'wpforo');
438
+ }
439
+ return $cache_plugins;
440
+ }
441
+
442
+ public function cache_plugins_status(){
443
+ $not_exluded = [];
444
+ $cache_plugins = $this->cache_plugins();
445
+ $excluded = wpforo_get_option( 'wpforo_excluded_cache', null, false );
446
+ $excluded = explode(',', $excluded );
447
+ if( ! empty( $cache_plugins ) ){
448
+ foreach( $cache_plugins as $cache_plugin ){
449
+ if( ! in_array( $cache_plugin['name'], $excluded ) ){
450
+ $not_exluded[] = $cache_plugin;
451
+ }
452
+ }
453
+ }
454
+ return $not_exluded;
455
+ }
456
+
457
+ }
classes/Feed.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+ // Exit if accessed directly
5
+ if( ! defined( 'ABSPATH' ) ) exit;
6
+
7
+ class Feed {
8
+ function rss2_url( $echo = true, $general = false ) {
9
+ $url = wpforo_get_request_uri();
10
+ $url = preg_replace( '|\?.+$|is', '', $url );
11
+ if( isset( WPF()->current_object['forumid'] ) ) {
12
+ $forumid = WPF()->current_object['forumid'];
13
+ }
14
+ if( isset( WPF()->current_object['topicid'] ) ) {
15
+ $topicid = WPF()->current_object['topicid'];
16
+ }
17
+ if( isset( $forumid ) && isset( $topicid ) ) {
18
+ $rss2 = $url . '?type=rss2&forum=' . intval( $forumid ) . '&topic=' . intval( $topicid );
19
+ } elseif( isset( $forumid ) && ! isset( $topicid ) ) {
20
+ $rss2 = $url . '?type=rss2&forum=' . intval( $forumid );
21
+ }
22
+
23
+ if( $general ) {
24
+ if( $general == 'topic' ) {
25
+ $rss2 = $url . '?type=rss2&forum=g&topic=g';
26
+ } elseif( $general == 'forum' ) {
27
+ $rss2 = $url . '?type=rss2&forum=g';
28
+ }
29
+ }
30
+
31
+ $rss2 = esc_url( $rss2 );
32
+
33
+ if( $echo ) {
34
+ echo $rss2;
35
+ } else {
36
+ return $rss2;
37
+ }
38
+ }
39
+
40
+ function rss2_forum( $forum = [], $topics = [] ) {
41
+ if( empty( $forum ) ) {
42
+ if( ! wpforo_setting( 'rss', 'feed' ) ) {
43
+ header( 'HTTP/1.0 404 Not Found', true, 404 );
44
+ die();
45
+ } else {
46
+ return;
47
+ }
48
+ }
49
+ header( "Content-Type: application/xml; charset" . get_option( 'blog_charset' ) );
50
+ echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?' . '>';
51
+ ?>
52
+ <rss version="2.0"
53
+ xmlns:atom="http://www.w3.org/2005/Atom"
54
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
55
+ xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
56
+ xmlns:admin="http://webns.net/mvcb/"
57
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
58
+ xmlns:content="http://purl.org/rss/1.0/modules/content/">
59
+ <channel>
60
+ <title>
61
+ <?php if( ! isset( $forum['title'] ) || ! $forum['title'] ): ?>
62
+ <?php echo esc_html( WPF()->board->get_current( 'settings' )['title'] ) . ' - ' . wpforo_phrase( 'Recent Topics', false ) ?>
63
+ <?php else: ?>
64
+ <?php echo esc_html( $forum['title'] ); ?> - <?php echo esc_html( WPF()->board->get_current( 'settings' )['title'] ); ?>
65
+ <?php endif; ?>
66
+ </title>
67
+ <link><?php echo esc_url( $forum['forumurl'] ); ?></link>
68
+ <description><?php echo esc_html( WPF()->board->get_current( 'settings' )['desc'] ); ?></description>
69
+ <language><?php bloginfo_rss( 'language' ); ?></language>
70
+ <lastBuildDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', date( 'Y-m-d H:i:s' ), false ); ?></lastBuildDate>
71
+ <generator>wpForo</generator>
72
+ <ttl>60</ttl>
73
+ <?php if( ! empty( $topics ) ): ?>
74
+ <?php foreach( $topics as $topic ): ?>
75
+ <item>
76
+ <title><?php echo wpforo_removebb( esc_html( $topic['title'] ) ); ?></title>
77
+ <link><?php echo esc_url( $topic['topicurl'] ); ?></link>
78
+ <pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', $topic['created'], false ); ?></pubDate>
79
+ <description><![CDATA[<?php echo wpforo_removebb( esc_html( $topic['description'] ) ) ?>]]></description>
80
+ <content:encoded><![CDATA[<?php echo wpforo_removebb( $topic['content'] ) ?>]]></content:encoded>
81
+ <?php if( $forum['forumurl'] != '#' ): ?>
82
+ <category domain="<?php echo esc_url( $forum['forumurl'] ); ?>"><?php echo esc_html( $forum['title'] ); ?></category><?php endif; ?>
83
+ <dc:creator><?php echo esc_html( $topic['author'] ); ?></dc:creator>
84
+ <guid isPermaLink="true"><?php echo esc_url( $topic['topicurl'] ); ?></guid>
85
+ </item>
86
+ <?php endforeach; ?>
87
+ <?php endif; ?>
88
+ </channel>
89
+ </rss>
90
+ <?php
91
+ exit();
92
+ }
93
+
94
+ function rss2_topic( $forum = [], $topic = [], $posts = [] ) {
95
+ if( empty( $forum ) ) {
96
+ if( ! wpforo_setting( 'rss', 'feed' ) ) {
97
+ header( 'HTTP/1.0 404 Not Found', true, 404 );
98
+ die();
99
+ } else {
100
+ return;
101
+ }
102
+ }
103
+ header( "Content-Type: application/xml; charset" . get_option( 'blog_charset' ) );
104
+ echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?' . '>';
105
+ ?>
106
+ <rss version="2.0"
107
+ xmlns:atom="http://www.w3.org/2005/Atom"
108
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
109
+ xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
110
+ xmlns:admin="http://webns.net/mvcb/"
111
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
112
+ xmlns:content="http://purl.org/rss/1.0/modules/content/">
113
+ <channel>
114
+ <title>
115
+ <?php if( ! isset( $topic['title'] ) || ! $topic['title'] ): ?>
116
+ <?php echo esc_html( WPF()->board->get_current( 'settings' )['title'] ) . ' - ' . wpforo_phrase( 'Recent Posts', false ); ?>
117
+ <?php else: ?>
118
+ <?php echo esc_html( $topic['title'] ); ?> - <?php echo esc_html( wpfval( $forum, 'title' ) ); ?>
119
+ <?php endif; ?>
120
+ </title>
121
+ <link><?php echo esc_url( $topic['topicurl'] ); ?></link>
122
+ <description><?php echo esc_html( WPF()->board->get_current( 'settings' )['desc'] ); ?></description>
123
+ <language><?php bloginfo_rss( 'language' ); ?></language>
124
+ <lastBuildDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', date( 'Y-m-d H:i:s' ), false ); ?></lastBuildDate>
125
+ <generator>wpForo</generator>
126
+ <ttl>60</ttl>
127
+ <?php if( ! empty( $posts ) ): ?>
128
+ <?php foreach( $posts as $post ): ?>
129
+ <item>
130
+ <title><?php echo wpforo_removebb( esc_html( $post['title'] ) ); ?></title>
131
+ <link><?php echo esc_url( $post['posturl'] ); ?></link>
132
+ <pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', $post['created'], false ); ?></pubDate>
133
+ <description><![CDATA[<?php echo wpforo_removebb( esc_html( $post['description'] ) ) ?>]]></description>
134
+ <content:encoded><![CDATA[<?php echo wpforo_removebb( $post['content'] ) ?>]]></content:encoded>
135
+ <?php if( wpfval( $forum, 'forumurl' ) != '#' ): ?>
136
+ <category domain="<?php echo esc_url( wpfval( $forum, 'forumurl' ) ); ?>"><?php echo esc_html( wpfval( $forum, 'title' ) ); ?></category><?php endif; ?>
137
+ <dc:creator><?php echo esc_html( $post['author'] ); ?></dc:creator>
138
+ <guid isPermaLink="true"><?php echo esc_url( $post['posturl'] ); ?></guid>
139
+ </item>
140
+ <?php endforeach; ?>
141
+ <?php endif; ?>
142
+ </channel>
143
+ </rss>
144
+ <?php
145
+ exit();
146
+ }
147
+
148
+ }
classes/Forms.php ADDED
@@ -0,0 +1,1522 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ // Exit if accessed directly
6
+ if( ! defined( 'ABSPATH' ) ) exit;
7
+
8
+ class Forms {
9
+ public $default;
10
+ public $user_dir;
11
+ public $field_dir;
12
+ public $current;
13
+
14
+ public function __construct() {
15
+ $this->init_defaults();
16
+ }
17
+
18
+ private function init_defaults() {
19
+ $this->user_dir = '/users/';
20
+ $this->field_dir = '/users/fields/';
21
+ $this->default = [
22
+ 'label' => '',
23
+ 'title' => '',
24
+ 'name' => '',
25
+ 'value' => '',
26
+ 'values' => '',
27
+ 'type' => 'text',
28
+ 'placeholder' => '',
29
+ 'description' => '',
30
+ 'id' => '',
31
+ 'class' => '',
32
+ 'attributes' => '',
33
+ 'isDefault' => 0,
34
+ 'isWrapItem' => '',
35
+ 'isLabelFirst' => '',
36
+ 'isDisabled' => 0,
37
+ 'isEditable' => 1,
38
+ 'isRequired' => 0,
39
+ 'isMultiChoice' => 0,
40
+ 'isOnlyForGuests' => 0,
41
+ 'isRemovable' => 1,
42
+ 'isSearchable' => 0,
43
+ 'allowedGroupIds' => [],
44
+ 'fileExtensions' => '',
45
+ 'fileSize' => 1,
46
+ 'minLength' => 0,
47
+ 'maxLength' => 0,
48
+ 'faIcon' => '',
49
+ 'html' => '',
50
+ 'varname' => '',
51
+ 'template' => '',
52
+ 'cantBeInactive' => [],
53
+ 'canEdit' => [ 1 ],
54
+ 'canView' => [ 1, 2, 3, 5 ],
55
+ 'can' => '',
56
+ 'isDisplayDefaultValues' => 0,
57
+ ];
58
+ }
59
+
60
+ public function fix_field( $field ) {
61
+ return wpforo_array_args_cast_and_merge( (array) $field, $this->default );
62
+ }
63
+
64
+ /**
65
+ * Form builder
66
+ * for form layout and field building
67
+ *
68
+ * @param array $fields associative multi-level array
69
+ *
70
+ * @return string form HTML
71
+ */
72
+ public function build( $fields ) {
73
+ if( empty( $fields ) ) return '';
74
+ $html = '';
75
+ foreach( $fields as $row_key => $row ) {
76
+ $row_class = "row-$row_key " . apply_filters( 'wpforo_row_classes', '', $row_key );
77
+ $html .= '<div class="wpf-tr ' . esc_attr( $row_class ) . '">';
78
+ foreach( $row as $col_key => $col ) {
79
+ $col_class_field = '';
80
+ foreach( $col as $field ) {
81
+ $col_class_field .= ' wpf-row-' . wpfval( $field, 'name' );
82
+ }
83
+ reset( $col );
84
+ $col_class = "row_$row_key-col_$col_key " . $col_class_field . ' ' . apply_filters( 'wpforo_col_classes', '', $row_key, $col_key );
85
+ $html .= '<div class="wpf-td wpfw-' . count( $row ) . ' ' . esc_attr( $col_class ) . '">';
86
+ foreach( $col as $field ) {
87
+ if( ! empty( $field ) ) $html .= $this->build_field( $field );
88
+ }
89
+ $html .= '</div>';
90
+ }
91
+ $html .= '<div class="wpf-cl"></div></div>';
92
+ }
93
+
94
+ return $html;
95
+ }
96
+
97
+ /**
98
+ * Builds field with input HTML and wrapper divs
99
+ *
100
+ * @param array $args field arguments
101
+ *
102
+ * @return string field HTML
103
+ */
104
+ public function build_field( $args ) {
105
+ if( is_string( $args ) ) $args = WPF()->member->get_field( $args );
106
+
107
+ $html = '';
108
+ if( ! is_array( $args ) || empty( $args ) ) return '';
109
+ $f = wpforo_parse_args( $args, $this->default );
110
+ $f = $this->prepare_args( $f );
111
+
112
+ //Get field input tag
113
+ $field_html = $this->field( $f );
114
+
115
+ //Wrapping field input
116
+ if( $f['template'] === 'register' ) {
117
+ if( $this->can_add( $f ) ) {
118
+ $html = $this->field_wrap_register( $field_html, $f );
119
+ }
120
+ } elseif( $f['template'] === 'account' ) {
121
+ if( $this->can_edit( $f ) ) {
122
+ $html = $this->field_wrap_account( $field_html, $f );
123
+ }
124
+ } elseif( $f['template'] === 'profile' ) {
125
+ if( $this->can_view( $f ) && $this->can_show_value( $f ) ) {
126
+ $f = $this->esc_field( $f );
127
+ $html = $this->field_wrap_profile( $field_html, $f );
128
+ }
129
+ } elseif( $f['template'] === 'members' ) {
130
+ if( $this->can_show_value( $f ) ) $html = $this->field_wrap_members( $field_html, $f );
131
+ } else {
132
+ $html = $this->field_wrap_default( $field_html, $f );
133
+ }
134
+
135
+ return $html;
136
+ }
137
+
138
+ /**
139
+ * Field input HTML builder
140
+ *
141
+ * @param array $f field arguments
142
+ *
143
+ * @return string field input HTML
144
+ */
145
+ public function field( $f ) {
146
+ $f = $this->prepare_field_args( $f );
147
+
148
+ $f = apply_filters( 'wpforo_form_field', $f );
149
+
150
+ switch( $f['type'] ) {
151
+ case 'url':
152
+ $field_html = $this->field_url( $f );
153
+ break;
154
+ case 'file':
155
+ $field_html = $this->field_file( $f );
156
+ break;
157
+ case 'html':
158
+ $field_html = $this->field_html( $f );
159
+ break;
160
+ case 'radio':
161
+ $field_html = $this->field_radio( $f );
162
+ break;
163
+ case 'select':
164
+ $field_html = $this->field_select( $f );
165
+ break;
166
+ case 'avatar':
167
+ $field_html = $this->field_avatar( $f );
168
+ break;
169
+ case 'textarea':
170
+ $field_html = $this->field_textarea( $f );
171
+ break;
172
+ case 'tinymce':
173
+ $field_html = $this->field_tinymce( $f );
174
+ break;
175
+ case 'password':
176
+ $field_html = $this->field_password( $f );
177
+ break;
178
+ case 'checkbox':
179
+ $field_html = $this->field_checkbox( $f );
180
+ break;
181
+ case 'usergroup':
182
+ $field_html = $this->field_usergroup( $f );
183
+ break;
184
+ case 'secondary_groups':
185
+ $field_html = $this->field_secondary_groups( $f );
186
+ break;
187
+ case 'user_nicename':
188
+ $field_html = $this->field_nicename( $f );
189
+ break;
190
+ case 'tel':
191
+ $field_html = $this->field_tel( $f );
192
+ break;
193
+ default:
194
+ $field_html = $this->field_text( $f );
195
+ }
196
+
197
+ return $field_html;
198
+ }
199
+
200
+ /**
201
+ * Wraps input HTML for Registration form
202
+ *
203
+ * @param string $field_html input HTML
204
+ * @param array $f field arguments
205
+ *
206
+ * @return string wrapped field input HTML
207
+ */
208
+ public function field_wrap_register( $field_html, $f ) {
209
+ $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr( $f['title'] ) . '">';
210
+ if( $f['type'] !== 'html' ) {
211
+ if( $f['label'] || $f['description'] ) {
212
+ $html .= '<div class="wpf-label-wrap">';
213
+ if( $f['label'] ) {
214
+ $html .= '<p class="wpf-label wpfcl-1">' . $f['label'] . $f['required_indicator'] . '</p>';
215
+ }
216
+ if( $f['description'] ) {
217
+ $html .= '<div class="wpf-desc wpfcl-2">' . $f['description'] . '</div>';
218
+ }
219
+ $html .= '</div>';
220
+ }
221
+ $html .= '<div class="wpf-field-wrap">';
222
+ if( $f['faIcon'] ) {
223
+ $html .= '<i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
224
+ }
225
+ $html .= $field_html;
226
+ $html .= '</div>';
227
+ } else {
228
+ $html .= $field_html;
229
+ }
230
+ $html .= '<div class="wpf-field-cl"></div></div>';
231
+
232
+ return $html;
233
+ }
234
+
235
+ /**
236
+ * Wraps input HTML for Account form
237
+ *
238
+ * @param string $field_html input HTML
239
+ * @param array $f field arguments
240
+ *
241
+ * @return string wrapped field input HTML
242
+ */
243
+ public function field_wrap_account( $field_html, $f ) {
244
+ $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr( $f['title'] ) . '">';
245
+ if( $f['type'] === 'html' ) {
246
+ $html .= $field_html;
247
+ } elseif( $f['name'] === 'user_login' ) {
248
+ $html .= '<div class="wpf-label-wrap">';
249
+ $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( $f['label'] ) . '</p>';
250
+ $html .= '</div>';
251
+ $html .= '<div class="wpf-field-wrap">';
252
+ $html .= '<span class="wpf-username">' . $f['value'] . '</span>';
253
+ $html .= '</div>';
254
+ } elseif( $f['type'] !== 'password' && ! $f['isEditable'] && ! $this->can_moderate( $f ) && WPF()->current_user_groupid !== 1 ) {
255
+ $f = $this->esc_field( $f );
256
+ $html .= '<div class="wpf-label-wrap">';
257
+ $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( $f['label'] ) . '</p>';
258
+ $html .= '</div>';
259
+ $html .= '<div class="wpf-field-wrap">';
260
+ $html .= '<span class="wpf-filed-value"><i class="' . esc_attr( $f['faIcon'] ) . '"></i> ' . $f['value'] . '</span>';
261
+ $html .= '</div>';
262
+ } else {
263
+ if( $f['label'] || $f['description'] ) {
264
+ $html .= '<div class="wpf-label-wrap">';
265
+ if( $f['label'] ) {
266
+ $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( $f['label'] ) . $f['required_indicator'] . '</p>';
267
+ }
268
+ if( $f['description'] ) {
269
+ $html .= '<div class="wpf-desc wpfcl-2">' . $f['description'] . '</div>';
270
+ }
271
+ $html .= '</div>';
272
+ }
273
+ $html .= '<div class="wpf-field-wrap">';
274
+ if( $f['faIcon'] ) {
275
+ $html .= '<i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
276
+ }
277
+ $html .= $field_html;
278
+ $html .= '</div>';
279
+ }
280
+ $html .= '<div class="wpf-field-cl"></div></div>';
281
+
282
+ return $html;
283
+ }
284
+
285
+ /**
286
+ * Wraps input HTML for Profile page
287
+ *
288
+ * @param string $field_html input HTML
289
+ * @param array $f field arguments
290
+ *
291
+ * @return string wrapped field input HTML
292
+ */
293
+ public function field_wrap_profile( $field_html, $f ) {
294
+
295
+ if( ! in_array( $f['type'], [ 'html', 'avatar' ] ) && ! ( $f['isRequired'] && $f['isDisplayDefaultValues'] ) && ( ! isset( $f['value'] ) || ( ! is_numeric( $f['value'] ) && empty( $f['value'] ) ) ) ) {
296
+ return false;
297
+ }
298
+
299
+ $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr( $f['title'] ) . '">';
300
+ if( $f['type'] !== 'html' ) {
301
+ if( ! $f['faIcon'] ) {
302
+ $f['faIcon'] = 'fas fa-address-card';
303
+ }
304
+ if( $f['label'] ) {
305
+ $html .= '<div class="wpf-label-wrap"><p class="wpf-label wpfcl-1"><i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i> ' . $f['label'] . '</p></div>';
306
+ }
307
+ if( $f['type'] === 'avatar' || ( isset( $f['value'] ) && ! empty( $f['value'] ) ) ) {
308
+ if( is_array( $f['value'] ) ) {
309
+ $html .= esc_html( implode( ', ', $f['value'] ) );
310
+ } else {
311
+ $f = $this->prepare_values( $f, WPF()->current_object['userid'] );
312
+ $html .= '<div class="wpf-field-wrap">';
313
+ $html .= $f['value'];
314
+ $html .= '</div>';
315
+ }
316
+ } else {
317
+ if( $default_values = wpforo_preg_grep_recursive( '#^\[.+?]$#isu', $f['values'] ) ) {
318
+ $_html = '';
319
+ foreach( $default_values as $default_key => $default_value ) {
320
+ if( ! is_array( $default_value ) ) {
321
+ $item = $this->build_item_value_lable( $default_value );
322
+ $_html .= $item['label'] . ', ';
323
+ } else {
324
+ $csv = '';
325
+ foreach( $default_value as $_default_value ) {
326
+ if( ! is_array( $_default_value ) ) {
327
+ $item = $this->build_item_value_lable( $_default_value );
328
+ $csv .= $item['label'] . ', ';
329
+ }
330
+ }
331
+ $_html .= trim( $default_key ) . '( ' . trim( $csv, ', ' ) . ' ), ';
332
+ }
333
+ }
334
+ $html .= sprintf( '<div class="wpf-field-wrap">%1$s</div>', trim( $_html, ', ' ) );
335
+ } else {
336
+ return false;
337
+ }
338
+ }
339
+ } else {
340
+ $html .= $field_html;
341
+ }
342
+ $html .= '<div class="wpf-field-cl"></div></div>';
343
+
344
+ return $html;
345
+ }
346
+
347
+ /**
348
+ * Wraps input HTML for Members Search form
349
+ *
350
+ * @param string $field_html input HTML
351
+ * @param array $f field arguments
352
+ *
353
+ * @return string wrapped field input HTML
354
+ */
355
+ public function field_wrap_members( $field_html, $f ) {
356
+ $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr( $f['title'] ) . '">';
357
+ if( $f['type'] === 'html' ) {
358
+ $html .= $field_html;
359
+ } else {
360
+ if( $f['label'] || $f['description'] ) {
361
+ $html .= '<div class="wpf-label-wrap">';
362
+ if( $f['label'] ) {
363
+ $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( $f['label'] ) . $f['required_indicator'] . '</p>';
364
+ }
365
+ if( $f['description'] ) {
366
+ $html .= '<div class="wpf-desc wpfcl-2">' . $f['description'] . '</div>';
367
+ }
368
+ $html .= '</div>';
369
+ }
370
+ $html .= '<div class="wpf-field-wrap">';
371
+ if( $f['faIcon'] ) {
372
+ $html .= '<i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
373
+ }
374
+ $html .= $field_html;
375
+ $html .= '</div>';
376
+ }
377
+ $html .= '<div class="wpf-field-cl"></div></div>';
378
+
379
+ return $html;
380
+ }
381
+
382
+ /**
383
+ * Default wrapper of input HTML
384
+ *
385
+ * @param string $field_html input HTML
386
+ * @param array $f field arguments
387
+ *
388
+ * @return string wrapped field input HTML
389
+ */
390
+ public function field_wrap_default( $field_html, $f ) {
391
+ $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '">';
392
+ if( $f['type'] === 'html' ) {
393
+ $html .= $field_html;
394
+ } else {
395
+ if( $f['label'] || $f['description'] ) {
396
+ $html .= '<div class="wpf-label-wrap" style="float: none; width: 100%;">';
397
+ if( $f['faIcon'] ) $html .= '<i class="' . esc_attr( $f['faIcon'] ) . '"></i>';
398
+ if( $f['label'] ) {
399
+ $html .= '<p class="wpf-label wpfcl-1" style="display: inline-block; margin-left: 5px;">' . stripslashes( $f['label'] ) . $f['required_indicator'] . '</p>';
400
+ }
401
+
402
+ if( wpfval( $f, 'type' ) === 'file' && ( $file_size = wpfval( $f, 'fileSize' ) ) ) {
403
+ $html .= '<em style="opacity: 0.8;"> (' . wpforo_phrase( 'max allowed file size', false, 'lower' ) . ' ' . $file_size . 'MB)</em>';
404
+ }
405
+
406
+ if( $f['description'] ) {
407
+ $html .= '<div class="wpf-desc wpfcl-2" style="display: inline-block; margin-left: 5px;">' . $f['description'] . '</div>';
408
+ }
409
+ $html .= '</div>';
410
+ } else {
411
+ if( $f['faIcon'] ) $html .= '<i class="' . esc_attr( $f['faIcon'] ) . '"></i>';
412
+ }
413
+ $html .= '<div class="wpf-field-wrap" style="width: 100%">';
414
+ $html .= $field_html;
415
+ $html .= '</div>';
416
+ }
417
+ $html .= '<div class="wpf-field-cl"></div></div>';
418
+
419
+ return $html;
420
+ }
421
+
422
+ /**
423
+ * File - Field builder
424
+ *
425
+ * @param array $f field arguments
426
+ *
427
+ * @return string field HTML
428
+ */
429
+ public function field_file( $f ) {
430
+ $field_html = '';
431
+ $extensions = '';
432
+ if( $f['fileExtensions'] ) {
433
+ foreach( $f['fileExtensions'] as $key => $ext ) {
434
+ if( strpos( $ext, '.' ) === false ) {
435
+ $f['fileExtensions'][ $key ] = '.' . $ext;
436
+ }
437
+ }
438
+ $f['fileExtensions'] = implode( ', ', $f['fileExtensions'] );
439
+ if( $f['fileExtensions'] ) $extensions = ' accept="' . esc_attr( $f['fileExtensions'] ) . '" ';
440
+ }
441
+ if( $f['value'] ) {
442
+ $f['isRequired'] = '';
443
+ if( is_array( $f['value'] ) ) {
444
+ $file_name = (string) wpfval( $f['value'], 'filename' );
445
+ $f['value'] = (string) wpfval( $f['value'], 'fileurl' );
446
+ } else {
447
+ $file_name = basename( $f['value'] );
448
+ }
449
+ $extension = pathinfo( $f['value'], PATHINFO_EXTENSION );
450
+ $f['value'] = wpforo_fix_upload_url( $f['value'] );
451
+
452
+ if( wpfval( WPF()->current_object['user'], 'userid' ) ) {
453
+ $url = strtok( wpforo_get_request_uri(), '?' );
454
+ $delete = $url . '?foro_f=' . $f['name'] . '&foro_u=' . WPF()->current_object['user']['userid'];
455
+ $delete_url = wp_nonce_url( wpforo_home_url( $delete ), 'wpforo_delete_profile_field', 'foro_n' );
456
+ $delete_html = ' &nbsp;|&nbsp; <a href="' . $delete_url . '" title="' . wpforo_phrase( 'Delete this file', false ) . '" onclick="return confirm(\'' . wpforo_phrase( 'Are you sure you want to delete this file?', false ) . '\')"><i class="fas fa-times" style="color:#e86666; font-size:14px; line-height:14px;"></i></a>';
457
+ } else {
458
+ $delete_html = ' &nbsp;|&nbsp; <span data-fieldkey="' . $f['fieldKey'] . '" style="cursor: pointer;" class="wpforo-delete-custom-file" title="' . wpforo_phrase( 'Delete this file', false ) . '"><i class="fas fa-times" style="color:#e86666; font-size:14px; line-height:14px;"></i></span>';
459
+ }
460
+
461
+ if( $extension && wpforo_is_image( $extension ) ) {
462
+ $field_html .= '<div class="wpf-field-file-wrap" style="margin-bottom: 10px;">
463
+ <img src="' . esc_url_raw( $f['value'] ) . '" alt="' . esc_attr( $file_name ) . '" title="' . esc_attr( $file_name ) . '" style="max-width:100px; max-height:100px"/>
464
+ <div class="wpf-field-file-name">
465
+ <i class="fas fa-paperclip"></i>
466
+ <a href="' . esc_url_raw( $f['value'] ) . '" title="' . esc_attr( $file_name ) . '" target="_blank">' . esc_attr( $file_name ) . '</a>' . $delete_html . '
467
+ </div>
468
+ </div>';
469
+ } else {
470
+ $field_html .= '<div class="wpf-field-file-wrap" style="margin-bottom: 10px;">
471
+ <div class="wpf-field-file-name">
472
+ <i class="fas fa-paperclip"></i>
473
+ <a href="' . esc_url_raw( $f['value'] ) . '" title="' . esc_attr( $file_name ) . '" target="_blank">' . esc_attr( $file_name ) . '</a>' . $delete_html . '
474
+ </div>
475
+ </div>';
476
+ }
477
+ }
478
+ $field_html .= '<input type="file" name="' . esc_attr( $f['fieldName'] ) . '" value=""
479
+ id="' . esc_attr( $f['fieldId'] ) . '" class="' . esc_attr( $f['fieldId'] ) . '"
480
+ ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $extensions . ' />';
481
+
482
+ return $field_html;
483
+ }
484
+
485
+ private function form_extra( $f ) {
486
+ $form_type = wpfval( $f, 'form_type' );
487
+ switch( $form_type ) {
488
+ case 'topic':
489
+ if( $forum = wpfval( $f, 'meta', 'forum' ) ) {
490
+ wpforo_topic_form_extra( (int) wpfval( $forum, 'forumid' ), (array) wpfval( $f, 'meta', 'values' ) );
491
+ }
492
+ break;
493
+ case 'post':
494
+ if( $topic = wpfval( $f, 'meta', 'topic' ) ) {
495
+ wpforo_reply_form_extra( $topic, (array) wpfval( $f, 'meta', 'values' ) );
496
+ }
497
+ break;
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Textarea - Field builder
503
+ *
504
+ * @param array $f field arguments
505
+ *
506
+ * @return string field HTML
507
+ */
508
+ public function field_textarea( $f ) {
509
+ $field_html = '<textarea name="' . esc_attr( $f['fieldName'] ) . '"
510
+ id="' . esc_attr( $f['fieldId'] ) . '"
511
+ class="' . esc_attr( $f['fieldId'] ) . '"
512
+ placeholder="' . esc_attr( $f['placeholder'] ) . '"
513
+ ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . '
514
+ ' . $f['minmax'] . ' data-isbody="' . intval( $f['fieldKey'] === 'body' ) . '">' . esc_textarea( $f['value'] ) . '</textarea>';
515
+ if( $f['fieldKey'] === 'body' ) {
516
+ ob_start();
517
+ $this->form_extra( $f );
518
+ $field_html .= ob_get_clean();
519
+ }
520
+
521
+ return $field_html;
522
+ }
523
+
524
+ /**
525
+ * Textarea - Field builder
526
+ *
527
+ * @param array $f field arguments
528
+ *
529
+ * @return string field HTML
530
+ */
531
+ public function field_tinymce( $f ) {
532
+ WPF()->current_object['load_tinymce'] = true;
533
+ $f['isRequired'] = '';
534
+ $f['attributes'] .= ' data-wpforoeditor="tinymce" ';
535
+ if( empty( $f['wp_editor_settings'] ) ) $f['wp_editor_settings'] = WPF()->tpl->editor_buttons();
536
+ $f['wp_editor_settings'] = apply_filters( 'wpforo_form_field_tinymce_editor_settings', $f['wp_editor_settings'], $f );
537
+ wp_localize_script( 'wpforo-frontend-js', 'wpforo_editor_settings_' . esc_attr( $f['fieldId'] ), $f['wp_editor_settings'] );
538
+
539
+ return $this->field_textarea( $f );
540
+ }
541
+
542
+ /**
543
+ * Password - Field builder
544
+ *
545
+ * @param array $f field arguments
546
+ *
547
+ * @return string field HTML
548
+ */
549
+ public function field_password( $f ) {
550
+ $field_html = '';
551
+ if( $f['template'] === 'account' ) {
552
+ $f['isRequired'] = 0;
553
+ $f['label'] = wpforo_phrase( 'Old password', false );
554
+ $f['description'] = '';
555
+ $field_html .= '<input type="password"
556
+ name="' . esc_attr( $f['varname'] ) . '[old_pass]"
557
+ value="" id="' . esc_attr( $f['fieldId'] ) . '-old"
558
+ class="' . esc_attr( $f['fieldId'] ) . '"
559
+ placeholder="' . esc_attr( wpforo_phrase( 'Old password', false ) ) . '"
560
+ ' . $f['isDisabled'] . ' ' . $f['attributes'] . '/>
561
+ <i class="fas fa-eye-slash wpf-show-password"></i>';
562
+ }
563
+ $p1 = '1';
564
+ $p2 = '2';
565
+ if( ! empty( $f['varname'] ) ) {
566
+ $f['fieldName'] = $f['varname'] . '[' . $f['name'] . $p1 . ']';
567
+ } else {
568
+ $f['fieldName'] = $f['name'] . $p1;
569
+ }
570
+ if( $f['template'] === 'account' ) {
571
+ $f['label'] = wpforo_phrase( 'New', false ) . ' ' . wpforo_phrase( $f['label'], false, 'lower' );
572
+ $f['placeholder'] = wpforo_phrase( 'New', false ) . ' ' . wpforo_phrase( $f['placeholder'], false, 'lower' );
573
+ }
574
+ if( in_array( $f['template'], ['account','register'], true ) ) {
575
+ if( $f['template'] === 'account' ) $field_html .= '<div style="position:relative"><i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
576
+ $field_html .= '<input type="password"
577
+ name="' . esc_attr( $f['fieldName'] ) . '"
578
+ value="" id="' . esc_attr( $f['fieldId'] ) . '-new1"
579
+ class="' . esc_attr( $f['fieldId'] ) . '"
580
+ placeholder="' . esc_attr( $f['placeholder'] ) . '"
581
+ ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
582
+ if( $f['template'] === 'register' ) {
583
+ $field_html .= '<i class="fas fa-eye-slash wpf-show-password"></i>';
584
+ }
585
+ if( $f['template'] === 'account' ) {
586
+ $field_html .= '</div>';
587
+ }
588
+ $f['label'] = wpforo_phrase( 'Confirm Password', false );
589
+ $f['placeholder'] = wpforo_phrase( 'Confirm Password', false );
590
+ $f['description'] = '';
591
+ $f['fieldName'] = ( ! empty( $f['varname'] ) ? $f['varname'] . '[' . $f['name'] . $p2 . ']' : $f['name'] . $p2 );
592
+ $field_html .= '<div style="position:relative"><i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
593
+ $field_html .= '<input type="password"
594
+ name="' . esc_attr( $f['fieldName'] ) . '"
595
+ value="" id="' . esc_attr( $f['fieldId'] ) . '-new2"
596
+ class="' . esc_attr( $f['fieldId'] ) . '"
597
+ placeholder="' . esc_attr( $f['placeholder'] ) . '"
598
+ ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
599
+ $field_html .= '</div>';
600
+ }
601
+
602
+ return $field_html;
603
+ }
604
+
605
+ /**
606
+ * Checkbox - Field builder
607
+ *
608
+ * @param array $f field arguments
609
+ *
610
+ * @return string field HTML
611
+ */
612
+ public function field_checkbox( $f ) {
613
+ $i = 0;
614
+ $field_html = '';
615
+ $f['value'] = $this->build_array_value( $f['value'], "\n" );
616
+ if( ! is_array( $f['values'] ) ) $f['values'] = $this->build_array_using_string_rows( $f['values'] );
617
+ if( ! empty( $f['values'] ) ) {
618
+ $item_field_name = $f['fieldName'] . '[]';
619
+ $use_default_selected = ( trim( $f['isRequired'] ) && ! $f['value'] );
620
+ $f['isRequired'] = ( count( $f['values'] ) == 1 ) ? $f['isRequired'] : '';
621
+ $field_html .= '<input type="hidden" name="' . esc_attr( $f['fieldName'] ) . '" value="">';
622
+ foreach( $f['values'] as $row ) {
623
+ $item = $this->build_item_value_lable( $row );
624
+ $id = $f['fieldId'] . '_' . ++ $i;
625
+ $field_html .= '<div class="wpf-field-item">';
626
+ $item_html = '<input type="checkbox" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' checked ' : '' ) : $this->check( $item['value'], $f['value'] ) ) . '
627
+ name="' . esc_attr( $item_field_name ) . '"
628
+ value="' . esc_attr( $item['value'] ) . '"
629
+ id="' . esc_attr( $id ) . '"
630
+ class="wpf-input-checkbox ' . esc_attr( $f['class'] ) . '"
631
+ ' . $f['isDisabled'] . ' ' . $f['isRequired'] . ' ' . $f['attributes'] . '>';
632
+ $field_html .= $this->build_label( $item['label'], $item_html, $f, $id );
633
+ $field_html .= '</div>';
634
+ }
635
+ }
636
+
637
+ return $field_html;
638
+ }
639
+
640
+ /**
641
+ * Radio - Field builder
642
+ *
643
+ * @param array $f field arguments
644
+ *
645
+ * @return string field HTML
646
+ */
647
+ public function field_radio( $f ) {
648
+ $i = 0;
649
+ $field_html = '';
650
+ $f['value'] = $this->build_array_value( $f['value'], "\n" );
651
+ if( ! is_array( $f['values'] ) ) $f['values'] = $this->build_array_using_string_rows( $f['values'] );
652
+ $use_default_selected = ( trim( $f['isRequired'] ) && ! $f['value'] );
653
+ if( ! empty( $f['values'] ) ) {
654
+ foreach( $f['values'] as $row ) {
655
+ $item = $this->build_item_value_lable( $row );
656
+ $id = $f['fieldId'] . '_' . ++ $i;
657
+ $field_html .= '<div class="wpf-field-item">';
658
+ $item_html = '<input type="radio" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' checked ' : '' ) : $this->check( $item['value'], $f['value'] ) ) . '
659
+ name="' . esc_attr( $f['fieldName'] ) . '"
660
+ value="' . esc_attr( $item['value'] ) . '"
661
+ id="' . esc_attr( $id ) . '"
662
+ class="wpf-input-radio ' . esc_attr( $f['class'] ) . '"
663
+ ' . $f['isDisabled'] . ' ' . $f['isRequired'] . ' ' . $f['attributes'] . ' />';
664
+ $field_html .= $this->build_label( $item['label'], $item_html, $f, $id );
665
+ $field_html .= '</div>';
666
+ }
667
+ }
668
+
669
+ return $field_html;
670
+ }
671
+
672
+ /**
673
+ * Radio - Field builder
674
+ *
675
+ * @param array $f field arguments
676
+ *
677
+ * @return string field HTML
678
+ */
679
+ public function field_select( $f ) {
680
+ $field_html = '';
681
+ $f['value'] = $this->build_array_value( $f['value'], "\n" );
682
+ if( ! is_array( $f['values'] ) ) $f['values'] = $this->build_array_using_string_rows( $f['values'] );
683
+ $use_default_selected = ( trim( $f['isRequired'] ) && ! $f['value'] );
684
+ if( ! empty( $f['values'] ) ) {
685
+ $field_html .= '<select name="' . esc_attr( $f['fieldName'] ) . '"
686
+ id="' . esc_attr( $f['fieldId'] ) . '"
687
+ class="' . esc_attr( $f['class'] ) . '"
688
+ ' . $f['isMultiChoice'] . ' ' . $f['isDisabled'] . '
689
+ ' . $f['isRequired'] . ' ' . $f['attributes'] . '>';
690
+ if( ! $use_default_selected ) {
691
+ $field_html .= apply_filters( 'wpforo_form_field_select_first_option', '<option value="">' . wpforo_phrase( '--- Choose ---', false ) . '</option>', $f );
692
+ }
693
+ foreach( $f['values'] as $optgroup => $row ) {
694
+ if( is_array( $row ) && ! empty( $row ) ) {
695
+ $field_html .= '<optgroup label="' . esc_attr( $optgroup ) . '">';
696
+ foreach( $row as $sub_row ) {
697
+ $item = $this->build_item_value_lable( $sub_row );
698
+ $item['value'] = ( $f['name'] === 'timezone' ) ? str_replace( ' ', '_', $item['value'] ) : $item['value'];
699
+ $field_html .= '<option value="' . esc_attr( $item['value'] ) . '" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' selected ' : '' ) : $this->select( $item['value'], $f['value'] ) ) . '>' . esc_html( $item['label'] ) . '</option>';
700
+ }
701
+ $field_html .= '</optgroup>';
702
+ } else {
703
+ $item = $this->build_item_value_lable( $row );
704
+ $item['value'] = ( $f['name'] === 'timezone' ) ? str_replace( ' ', '_', $item['value'] ) : $item['value'];
705
+ $field_html .= '<option value="' . esc_attr( $item['value'] ) . '" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' selected ' : '' ) : $this->select( $item['value'], $f['value'] ) ) . '>' . esc_html( $item['label'] ) . '</option>';
706
+ }
707
+ }
708
+ $field_html .= '</select>';
709
+ }
710
+
711
+ return $field_html;
712
+ }
713
+
714
+ /**
715
+ * Usergroup - Field builder
716
+ *
717
+ * @param array $f field arguments
718
+ *
719
+ * @return string field HTML
720
+ */
721
+ public function field_usergroup( $f ) {
722
+ $field_html = '';
723
+ if( ! empty( $f['allowedGroupIds'] ) ) {
724
+ $field_html .= '<select name="' . esc_attr( $f['fieldName'] ) . '"
725
+ id="' . esc_attr( $f['fieldId'] ) . '"
726
+ class="' . esc_attr( $f['class'] ) . '"
727
+ ' . $f['isDisabled'] . ' ' . $f['isRequired'] . ' ' . $f['attributes'] . '>';
728
+
729
+ $field_html .= '<option value="">' . wpforo_phrase( '--- Choose ---', false ) . '</option>';
730
+ foreach( $f['allowedGroupIds'] as $groupid ) {
731
+ if( $f['value'] != 4 && $groupid == 4 ) continue;
732
+ if( $group = WPF()->usergroup->get_usergroup( $groupid ) ) {
733
+ $field_html .= '<option value="' . esc_attr( $groupid ) . '" ' . $this->select( $groupid, $f['value'] ) . '>' . $group['name'] . '</option>';
734
+ }
735
+ }
736
+ $field_html .= '</select>';
737
+ }
738
+
739
+ return $field_html;
740
+ }
741
+
742
+ public function field_secondary_groups( $f ) {
743
+ $field_html = '';
744
+ if( $groups = WPF()->usergroup->get_secondary_usergroups() ) {
745
+ $allowed_groupids = (array) wpfval( $f, 'allowedGroupIds' );
746
+ $i = 0;
747
+ $field_html .= '<input type="hidden" name="' . esc_attr( $f['fieldName'] ) . '" value="">';
748
+ foreach( $groups as $group ) {
749
+ if( in_array( $group['groupid'], [ 1, 2, 4 ] ) || ( $allowed_groupids && ! in_array( $group['groupid'], $allowed_groupids ) ) ) continue;
750
+ $id = $f['fieldId'] . '_' . ++ $i;
751
+ $field_html .= '<div class="wpf-field-item">';
752
+ $item_html = '<input type="checkbox" ' . $this->check( $group['groupid'], $f['value'] ) . '
753
+ name="' . esc_attr( $f['fieldName'] . '[]' ) . '"
754
+ value="' . esc_attr( $group['groupid'] ) . '"
755
+ id="' . esc_attr( $id ) . '"
756
+ class="wpf-input-checkbox ' . esc_attr( $f['class'] ) . '"
757
+ ' . $f['isDisabled'] . ' ' . $f['attributes'] . '>';
758
+ $field_html .= $this->build_label( $group['name'], $item_html, $f, $id );
759
+ $field_html .= '</div>';
760
+ }
761
+ }
762
+
763
+ return $field_html;
764
+ }
765
+
766
+ /**
767
+ * Avatar - Field builder
768
+ *
769
+ * @param array $f field arguments
770
+ *
771
+ * @return string field HTML
772
+ */
773
+ public function field_avatar( $f ) {
774
+ $remote_url = ( $f['value'] && strpos( $f['value'], 'wpforo/avatars' ) === false ) ? $f['value'] : '';
775
+ $field_html = '<ul>
776
+ <li>
777
+ <input ' . $f['isRequired'] . ' name="' . esc_attr( $f['varname'] ) . '[avatar_type]" id="wpfat_gravatar" value="gravatar" ' . ( $f['value'] == '' || $f['value'] == null ? 'checked="checked"' : '' ) . ' type="radio" />&nbsp;
778
+ <label for="wpfat_gravatar">' . wpforo_phrase( 'Default avatar', false ) . '</label>
779
+ </li>
780
+ <li>
781
+ <input name="' . esc_attr( $f['varname'] ) . '[avatar_type]" id="wpfat_remote" value="remote" ' . ( $f['value'] && strpos( $f['value'], 'wpforo/avatars' ) === false ? 'checked="checked"' : '' ) . ' type="radio" />&nbsp;
782
+ <label for="wpfat_remote">' . wpforo_phrase( 'Specify avatar by URL:', false ) . '</label>
783
+ <input autocomplete="off" name="' . esc_attr( $f['varname'] ) . '[avatar_url]" value="' . esc_url( $remote_url ) . '" maxlength="300" data-wpfucf-minmaxlength="1,300" type="url" />
784
+ </li>';
785
+ if( ( wpfval( WPF()->current_object, 'template' ) && WPF()->current_object['template'] === 'register' ) || WPF()->usergroup->can( 'upa' ) ) {
786
+ if( strpos( $f['value'], 'gravatar.com' ) === false && strpos( $f['value'], 'facebook.com' ) === false ) {
787
+ $url = $f['value'] . '?lm=' . time();
788
+ }else{
789
+ $url = '';
790
+ }
791
+ $field_html .= '<li>
792
+ <input name="' . esc_attr( $f['varname'] ) . '[avatar_type]" id="wpfat_custom" value="custom" type="radio" ' . ( ( strpos( $url, 'wpforo/avatars' ) !== false ) ? 'checked' : '' ) . ' />&nbsp;
793
+ <label for="wpfat_custom"> ' . wpforo_phrase( 'Upload an avatar', false ) . '</label>' . ( strpos( $url, 'wpforo/avatars' ) !== false ? '<br />
794
+ <img src="' . esc_url( $url ) . '" class="wpf-custom-avatar-img" alt="avatar">' : '' ) . '&nbsp;
795
+ <input class="wpf-custom-avatar" name="avatar" type="file" />&nbsp;</li>';
796
+ }
797
+ $field_html .= '</ul> <script type="text/javascript">jQuery(document).ready(function($){$( "input[name=\'' . esc_attr( $f['varname'] ) . '\[avatar_url\]\']" ).on("click", function(){$( "#wpfat_remote" ).prop(\'checked\', true);}); $( "input[name=\'avatar\']" ).on("click", function(){$( "#wpfat_custom" ).prop(\'checked\', true);});});</script>';
798
+
799
+ return $field_html;
800
+ }
801
+
802
+ /**
803
+ * HTML - Field builder
804
+ *
805
+ * @param array $f field arguments
806
+ *
807
+ * @return string field HTML
808
+ */
809
+ public function field_html( $f ) {
810
+ return stripslashes( do_shortcode( wpforo_apply_ucf_shortcode( $f['html'] ) ) );
811
+ }
812
+
813
+ /**
814
+ * URL - Field builder
815
+ *
816
+ * @param array $f field arguments
817
+ *
818
+ * @return string field HTML
819
+ */
820
+ public function field_url( $f ) {
821
+ return '<input type="url" value="' . esc_url_raw( $f['value'] ) . '"
822
+ name="' . esc_attr( $f['fieldName'] ) . '"
823
+ id="' . esc_attr( $f['fieldId'] ) . '"
824
+ class="' . esc_attr( $f['class'] ) . '"
825
+ placeholder="' . esc_attr( $f['placeholder'] ) . '"
826
+ ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
827
+ }
828
+
829
+ /**
830
+ * Nickname - Field builder
831
+ *
832
+ * @param array $f field arguments
833
+ *
834
+ * @return string field HTML
835
+ */
836
+ public function field_nicename( $f ) {
837
+ return '<input type="text" value="' . esc_attr( urldecode( $f['value'] ) ) . '"
838
+ name="' . esc_attr( $f['fieldName'] ) . '"
839
+ id="' . esc_attr( $f['fieldId'] ) . '"
840
+ class="' . esc_attr( $f['class'] ) . '"
841
+ placeholder="' . esc_attr( $f['placeholder'] ) . '"
842
+ ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
843
+ }
844
+
845
+ /**
846
+ * Text - Field builder
847
+ *
848
+ * @param array $f field arguments
849
+ *
850
+ * @return string field HTML
851
+ */
852
+ public function field_text( $f ) {
853
+ return '<input type="' . esc_attr( $f['type'] ) . '"
854
+ value="' . esc_attr( $f['value'] ) . '"
855
+ name="' . esc_attr( $f['fieldName'] ) . '"
856
+ id="' . esc_attr( $f['fieldId'] ) . '"
857
+ class="' . esc_attr( $f['class'] ) . '"
858
+ placeholder="' . esc_attr( $f['placeholder'] ) . '"
859
+ ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
860
+ }
861
+
862
+ public function field_tel( $f ) {
863
+ return '<input type="' . esc_attr( $f['type'] ) . '"
864
+ value="' . esc_attr( $f['value'] ) . '"
865
+ name="' . esc_attr( $f['fieldName'] ) . '"
866
+ id="' . esc_attr( $f['fieldId'] ) . '"
867
+ class="' . esc_attr( $f['class'] ) . '"
868
+ placeholder="' . esc_attr( $f['placeholder'] ) . '"
869
+ ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '
870
+ pattern="[0-9]*">';
871
+ }
872
+
873
+ /**
874
+ * Prepares displayed values for Profile fields
875
+ *
876
+ * @param array $f field arguments
877
+ * @param int $userid
878
+ *
879
+ * @return array prepared values
880
+ */
881
+ public function prepare_values( $f, $userid = 0 ) {
882
+ switch( $f['type'] ) {
883
+ case 'textarea':
884
+ $f['value'] = wpforo_kses( wpforo_decode( $f['value'] ) );
885
+ break;
886
+ case 'date':
887
+ // $f['value'] = wpforo_date($f['value'], 'date', false);
888
+ break;
889
+ case 'datetime':
890
+ $f['value'] = wpforo_date( $f['value'], 'datetime', false );
891
+ break;
892
+ case 'url':
893
+ $f['value'] = sprintf( '<a href="%1$s" target="_blank" rel="nofollow">%2$s</a>', $f['value'], $f['value'] );
894
+ break;
895
+ case 'email':
896
+ $f['value'] = sprintf( '<a href="mailto:%1$s" rel="nofollow">%2$s</a>', $f['value'], $f['value'] );
897
+ break;
898
+ case 'phone':
899
+ $f['value'] = sprintf( '<a href="tel:%1$s" rel="nofollow">%2$s</a>', $f['value'], $f['value'] );
900
+ break;
901
+ case 'file':
902
+ if( ! empty( $f['value'] ) ) {
903
+ if( is_array( $f['value'] ) ) {
904
+ $file_name = (string) wpfval( $f['value'], 'filename' );
905
+ $f['value'] = (string) wpfval( $f['value'], 'fileurl' );
906
+ } else {
907
+ $file_name = basename( $f['value'] );
908
+ }
909
+ $f['value'] = wpforo_fix_upload_url( $f['value'] );
910
+ $file_url = esc_url_raw( $f['value'] );
911
+ $file_name = esc_attr( $file_name );
912
+ $extension = pathinfo( $f['value'], PATHINFO_EXTENSION );
913
+ if( wpforo_is_image( $extension ) ) {
914
+ $f['value'] = sprintf( '<a href="%1$s" target="_blank" title="%2$s"><img src="%1$s" alt="%2$s" class="wpf-field-file-img" style="max-width:120px; max-height:120px" /></a>', $file_url, $file_name );
915
+ } elseif( wpforo_is_audio( $extension ) ) {
916
+ $f['value'] = sprintf( '<audio src="%1$s" controls title="%2$s"></audio>', $file_url, $file_name );
917
+ } elseif( wpforo_is_video( $extension ) ) {
918
+ $f['value'] = sprintf( '<video src="%1$s" controls title="%2$s"></video>', $file_url, $file_name );
919
+ } else {
920
+ $f['value'] = sprintf( '<a href="%s" target="_blank">%s</a>', $file_url, $file_name );
921
+ }
922
+ }
923
+ break;
924
+ case 'avatar':
925
+ $f['value'] = ( WPF()->usergroup->can( 'va' ) && wpforo_setting( 'profiles', 'avatars' ) ) ? WPF()->member->get_avatar_html( $f['value'], $userid ) : '';
926
+ break;
927
+ case 'color':
928
+ if( $f['value'] ) {
929
+ $f['value'] = '<input type="color" value="' . $f['value'] . '" disabled title="' . $f['value'] . '" style="min-width: 100px; min-height: 25px;">';
930
+ }
931
+ break;
932
+ }
933
+ switch( $f['name'] ) {
934
+ case 'skype':
935
+ $f['value'] = sprintf( '<a href="skype:%s?userinfo" rel="nofollow">%s</a>', $f['value'], $f['value'] );
936
+ break;
937
+ case 'location':
938
+ $f['value'] = sprintf( '<a href="//maps.google.com/?q=%s" target="_blank" rel="nofollow">%s</a>', $f['value'], $f['value'] );
939
+ break;
940
+ case 'signature':
941
+ $f['value'] = wpforo_signature( $f['value'], [ 'echo' => 0 ] );
942
+ break;
943
+ case 'about':
944
+ $f['value'] = wpforo_nofollow_tag( $f['value'] );
945
+ break;
946
+ }
947
+
948
+ return apply_filters( 'wpforo_form_prepare_values', $f );
949
+ }
950
+
951
+ /**
952
+ * Prepares arguments
953
+ *
954
+ * @param array $f field arguments
955
+ *
956
+ * @return array prepared values
957
+ */
958
+ public function prepare_args( $f ) {
959
+
960
+ $is_owner = $this->owner();
961
+
962
+ //field_class
963
+ $f['field_class'] = sanitize_text_field( $f['name'] );
964
+
965
+ //varname
966
+ $f['varname'] = $f['isDefault'] ? (string) wpfval( $this->current, 'varname' ) : 'data';
967
+
968
+ //template
969
+ $f['template'] = ( isset( $this->current['template'] ) ) ? $this->current['template'] : WPF()->current_object['template'];
970
+
971
+ //value
972
+ $f['value'] = ( isset( $this->current['value'][ $f['name'] ] ) ) ? $this->current['value'][ $f['name'] ] : $f['value'];
973
+ if( ! $f['isDefault'] && $f['varname'] ) {
974
+ $f['value'] = ( isset( $this->current['value'][ $f['varname'] ][ $f['name'] ] ) ) ? $this->current['value'][ $f['varname'] ][ $f['name'] ] : $f['value'];
975
+ }
976
+ $f['value'] = wpforo_unslashe( $f['value'] );
977
+ $f['value'] = wpforo_decode( $f['value'] );
978
+
979
+ if( $f['name'] === 'user_nicename' ) {
980
+ $f['value'] = urldecode( $f['value'] );
981
+ }
982
+
983
+ //allowedGroupIds
984
+ $groups = [];
985
+ if( ! empty( $f['allowedGroupIds'] ) ) $groups = $this->build_array_value( $f['allowedGroupIds'] );
986
+ if( $f['type'] === 'usergroup' ) {
987
+ if( ! $is_owner && wpforo_current_user_is( 'admin' ) ) {
988
+ $groups = WPF()->usergroup->get_usergroups( 'groupid' );
989
+ } elseif( $is_owner && ! in_array( WPF()->current_user_groupid, $f['allowedGroupIds'] ) ) {
990
+ $groups = [];
991
+ }
992
+ }
993
+ $f['allowedGroupIds'] = array_filter( $groups );
994
+
995
+ //isRequired
996
+ if( $f['isRequired'] ) {
997
+ $f['required_class'] = ' wpf-field-required ';
998
+ $f['required_indicator'] = ' <span class="wpf-field-required-icon" title="' . esc_attr( wpforo_phrase( 'Required field', false ) ) . '">*</span>';
999
+ } else {
1000
+ $f['required_class'] = '';
1001
+ $f['required_indicator'] = '';
1002
+ }
1003
+
1004
+ return $f;
1005
+ }
1006
+
1007
+ /**
1008
+ * @param array $f field arguments
1009
+ *
1010
+ * @return array prepared values
1011
+ */
1012
+ public function prepare_field_args( $f ) {
1013
+ //faIcon
1014
+ $f['faIcon'] = trim( $f['faIcon'] );
1015
+ if( strpos( $f['faIcon'], ' ' ) === false ) $f['faIcon'] = 'fas ' . $f['faIcon'];
1016
+
1017
+ //isRequired
1018
+ $f['isRequired'] = ( $f['isRequired'] ) ? ' required="required" ' : '';
1019
+
1020
+ //isDisabled
1021
+ $f['isDisabled'] = ( $f['isDisabled'] ) ? ' disabled="disabled" ' : '';
1022
+
1023
+ //fieldName | new key in args
1024
+ $f['fieldName'] = ( ! empty( $f['varname'] ) ? $f['varname'] . '[' . $f['name'] . ']' : $f['name'] );
1025
+
1026
+ //isMultiChoice
1027
+ if( $f['isMultiChoice'] ) {
1028
+ $f['fieldName'] .= '[]';
1029
+ $f['isMultiChoice'] = ' multiple="multiple" ';
1030
+ } else {
1031
+ $f['isMultiChoice'] = '';
1032
+ }
1033
+
1034
+ //fieldId | new key in args
1035
+ $f['fieldId'] = ( $f['varname'] ? $f['varname'] . '_' : '' ) . ( $f['id'] ?: $f['name'] );
1036
+ $f['fieldId'] = uniqid( $f['fieldId'] . '_' );
1037
+
1038
+ //minLength & maxLength
1039
+ $f['minLength'] = ( $f['minLength'] ) ? intval( $f['minLength'] ) : '';
1040
+ $f['maxLength'] = ( $f['maxLength'] ) ? intval( $f['maxLength'] ) : '';
1041
+ if( $f['minLength'] ) {
1042
+ $minLength_attr = ( $f['type'] === 'date' || $f['type'] === 'number' || $f['type'] === 'range' ) ? ' min="' . $f['minLength'] . '" ' : ' minlength="' . $f['minLength'] . '" ';
1043
+ }
1044
+ if( $f['maxLength'] ) {
1045
+ $maxLength_attr = ( $f['type'] === 'date' || $f['type'] === 'number' || $f['type'] === 'range' ) ? ' max="' . $f['maxLength'] . '" ' : ' maxlength="' . $f['maxLength'] . '" ';
1046
+ }
1047
+ $f['minmax'] = isset( $minLength_attr ) ? $minLength_attr : '';
1048
+ $f['minmax'] .= isset( $maxLength_attr ) ? ' ' . $maxLength_attr : '';
1049
+
1050
+ //attributes
1051
+ $f['attributes'] .= ' autocomplete="off"';
1052
+
1053
+ if( wpfkey( $f, 'fileExtensions' ) ) {
1054
+ if( is_scalar( $f['fileExtensions'] ) ) $f['fileExtensions'] = explode( ',', $f['fileExtensions'] );
1055
+ $f['fileExtensions'] = array_filter( $f['fileExtensions'] );
1056
+ }
1057
+
1058
+ return $f;
1059
+ }
1060
+
1061
+ /**
1062
+ * @param array $file_data
1063
+ * @param int $userid
1064
+ *
1065
+ * @return array
1066
+ */
1067
+ public function prepare_file_args( $file_data, $userid = 1 ) {
1068
+ $file = [ 'files' => [], 'fields' => [] ];
1069
+ $userid = $userid ?: WPF()->current_userid;
1070
+
1071
+ if( ! empty( $file_data ) ) {
1072
+ foreach( $file_data as $file_field_name => $file_name ) {
1073
+ $field_name_folder = substr( $file_field_name, 6 );
1074
+ $file_upload_dir = wpforo_fix_dir_sep( WPF()->folders['upload']['dir'] . $this->field_dir . $userid . DIRECTORY_SEPARATOR . $field_name_folder . DIRECTORY_SEPARATOR );
1075
+ $file_path = $file_upload_dir . $file_name;
1076
+ $file['files'][ $file_field_name ] = $file_path;
1077
+ $file['fields'][ $file_field_name ] = $this->field_dir . $userid . '/' . $field_name_folder . '/' . $file_name;
1078
+ wp_mkdir_p( $file_upload_dir );
1079
+ }
1080
+ }
1081
+
1082
+ return $file;
1083
+ }
1084
+
1085
+ public function check( $needle, $haystack ) {
1086
+ if( is_scalar( $haystack ) ) $haystack = explode( ',', $haystack );
1087
+
1088
+ return in_array( $needle, (array) $haystack ) ? 'checked' : '';
1089
+ }
1090
+
1091
+ public function select( $needle, $haystack ) {
1092
+ return in_array( $needle, (array) $haystack ) ? 'selected' : '';
1093
+ }
1094
+
1095
+ public function build_label( $item_label, $item_html, $f, $for = '' ) {
1096
+ $label = '<label for="' . $for . '" class="wpf-' . $f['type'] . '-label">' . stripslashes( $item_label ) . '</label>';
1097
+ $field_html = $f['isLabelFirst'] ? $label . '&nbsp;' . $item_html : $item_html . '&nbsp;' . $label;
1098
+ if( $f['isWrapItem'] ) $field_html = '<label for="' . $for . '">' . $field_html . '</label>';
1099
+
1100
+ return $field_html;
1101
+ }
1102
+
1103
+ public function build_item_value_lable( $string, $sep = '=>' ) {
1104
+ $string = trim( $string );
1105
+ $count = 0;
1106
+ $string = preg_replace( '#^\[(.+?)]$#isu', '$1', $string, 1, $count );
1107
+ $default_selected = (bool) $count;
1108
+ $data = explode( $sep, $string );
1109
+ $value = wpfkey( $data, 0 ) ? trim( $data[0] ) : 'no_value';
1110
+ $label = wpfkey( $data, 1 ) ? trim( $data[1] ) : $value;
1111
+
1112
+ return compact( 'value', 'label', 'default_selected' );
1113
+ }
1114
+
1115
+ public function build_array_value( $var, $sep = ',' ) {
1116
+ if( is_scalar( $var ) ) {
1117
+ $var = trim( $var );
1118
+ if( ! strlen( $var ) ) return [];
1119
+ }
1120
+ if( is_serialized( $var ) ) {
1121
+ $var = unserialize( $var );
1122
+ } elseif( is_scalar( $var ) && strpos( $var, $sep ) !== false ) {
1123
+ $var = explode( $sep, $var );
1124
+ }
1125
+
1126
+ return array_map( 'trim', (array) $var );
1127
+ }
1128
+
1129
+ public function build_array_using_string_rows( $string, $regexp = '' ) {
1130
+ if( ! $regexp ) $regexp = '#' . preg_quote( PHP_EOL ) . '#isu';
1131
+ $array = preg_split( $regexp, $string );
1132
+
1133
+ return array_filter( $array );
1134
+ }
1135
+
1136
+ public function sanitize( $data, $fields ) {
1137
+ $types = $this->field_types( $fields );
1138
+ if( ! empty( $data ) && ! empty( $types ) ) {
1139
+ foreach( $data as $name => $value ) {
1140
+ if( wpfval( $types, $name ) ) {
1141
+ $data[ $name ] = $this->sanitize_field( $value, $types[ $name ], $name );
1142
+ }
1143
+ }
1144
+ }
1145
+
1146
+ return $data;
1147
+ }
1148
+
1149
+ public function sanitize_field( $value, $type = 'text', $name = '' ) {
1150
+ if( ! is_null( $value ) ) {
1151
+ if( $type === 'text' ) {
1152
+ $value = sanitize_text_field( $value );
1153
+ if( $name === 'user_nicename' ) {
1154
+ $value = sanitize_title( sanitize_user( $value, true ) );
1155
+ }
1156
+ } elseif( $type === 'url' ) {
1157
+ $value = esc_url_raw( $value );
1158
+ } elseif( $type === 'date' ) {
1159
+ $value = sanitize_text_field( $value );
1160
+ } elseif( $type === 'textarea' ) {
1161
+ $value = stripslashes( wpforo_kses( trim( $value ), 'user_description' ) );
1162
+ } elseif( $type === 'email' ) {
1163
+ $value = sanitize_email( $value );
1164
+ } elseif( $type === 'password' ) {
1165
+ $value = trim( $value );
1166
+ } elseif( $type === 'usergroup' ) {
1167
+ $value = intval( $value );
1168
+ } elseif( $type === 'radio' ) {
1169
+ $value = sanitize_text_field( $value );
1170
+ } elseif( $type === 'checkbox' ) {
1171
+ if( $name === 'secondary_groupids' ) {
1172
+ $value = wpforo_sanitize_int( $value );
1173
+ } else {
1174
+ $value = wpforo_sanitize_text( $value );
1175
+ }
1176
+ } elseif( $type === 'select' ) {
1177
+ $value = sanitize_text_field( $value );
1178
+ } elseif( $type === 'color' ) {
1179
+ $value = sanitize_text_field( $value );
1180
+ } elseif( $type === 'number' ) {
1181
+ $value = intval( $value );
1182
+ } elseif( $type === 'tel' ) {
1183
+ $value = sanitize_text_field( $value );
1184
+ } elseif( $type === 'html' ) {
1185
+ $value = preg_replace( '#<script(.*?)>(.*?)</script>#is', '', $value );
1186
+ }
1187
+
1188
+ if( is_string( $value ) ) {
1189
+ $value = stripslashes( $value );
1190
+ }
1191
+ }
1192
+
1193
+ return $value;
1194
+ }
1195
+
1196
+ public function esc_field( $f ) {
1197
+ if( wpfkey( $f, 'value' ) ) {
1198
+ $f['value'] = wpforo_trim( $f['value'] );
1199
+ if( in_array( wpfval( $f, 'type' ), [ 'textarea', 'tinymce' ], true ) ) {
1200
+ $f['value'] = wpautop( wpforo_kses( stripslashes( $f['value'] ) ) );
1201
+ } elseif( $f['name'] === 'timezone' ) {
1202
+ $f['value'] = str_replace( '_', ' ', $f['value'] );
1203
+ } elseif( $f['fieldKey'] === 'secondary_groupids' ) {
1204
+ $f['value'] = WPF()->usergroup->get_secondary_group_names( $f['value'] );
1205
+ $f['value'] = implode( ', ', $f['value'] );
1206
+ } elseif( $f['type'] === 'usergroup' && $f['fieldKey'] === 'groupid' && ( $f['value'] = intval( $f['value'] ) ) ) {
1207
+ if( $group = WPF()->usergroup->get_usergroup( $f['value'] ) ) $f['value'] = wpfval( $group, 'name' );
1208
+ } elseif( is_array( $f['value'] ) ) {
1209
+ $f['value'] = array_filter( $f['value'] );
1210
+ $f['value'] = implode( ', ', $f['value'] );
1211
+ } else {
1212
+ $f['value'] = esc_html( $f['value'] );
1213
+ }
1214
+ }
1215
+ $f['value'] = apply_filters( 'wpforo_display_field_value', $f['value'], $f );
1216
+
1217
+ return $f;
1218
+ }
1219
+
1220
+ public function validate( &$data, &$fields ) {
1221
+ $type = '';
1222
+ $label = '';
1223
+ $userid = '';
1224
+ $error = [];
1225
+ $return = [];
1226
+ $is_owner = $this->owner();
1227
+ $template = ( isset( $this->current['template'] ) ) ? $this->current['template'] : WPF()->current_object['template'];
1228
+ if( empty( $data ) ) $error[] = 'No data submitted';
1229
+ if( empty( $fields ) ) $error[] = 'User profile fields not found';
1230
+ if( empty( $error ) ) {
1231
+ foreach( $fields as $r_key => $rows ) {
1232
+ foreach( $rows as $c_key => $cols ) {
1233
+ foreach( $cols as $key => $field ) {
1234
+ if( wpfval( $field, 'name' ) ) {
1235
+ $name = $field['name'];
1236
+ if( ! $template && wpfval( $field, 'template' ) ) $template = $field['template'];
1237
+ if( wpfval( $field, 'label' ) ) $label = esc_html( $field['label'] );
1238
+ if( wpfval( $field, 'type' ) ) $type = $field['type'];
1239
+ if( wpfval( $data, 'userid' ) ) $userid = $data['userid'];
1240
+ if( $template && $template !== 'register' ) {
1241
+ if( $template === 'account' && $field['type'] === 'password' ) $field['isRequired'] = 0;
1242
+ if( ! $this->can_edit( $field ) ) {
1243
+ unset( $cols[ $key ] );
1244
+ unset( $data[ $name ] );
1245
+ unset( $fields[ $r_key ][ $c_key ][ $key ] );
1246
+ }
1247
+ }
1248
+ if( wpfkey( $cols, $key ) && wpfkey( $data, $name ) ) {
1249
+ $value = $data[ $name ];
1250
+ if( is_string( $value ) ) {
1251
+ $value = trim( $value );
1252
+ $value = htmlspecialchars_decode( $value );
1253
+ }
1254
+ if( wpfval( $field, 'isRequired' ) && ! $value ) {
1255
+ $error[] = $label . ' ' . wpforo_phrase( 'field is required', false, false );
1256
+ }
1257
+ if( $value ) {
1258
+ if( $type === 'number' ) {
1259
+ if( wpfval( $field, 'minLength' ) ) {
1260
+ if( (int) $value < $field['minLength'] ) {
1261
+ $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field value must be at least %d', false, false ), intval( $field['minLength'] ) );
1262
+ }
1263
+ }
1264
+ if( wpfval( $field, 'maxLength' ) ) {
1265
+ if( (int) $value > $field['maxLength'] ) {
1266
+ $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field value cannot be greater than %d', false, false ), intval( $field['maxLength'] ) );
1267
+ }
1268
+ }
1269
+ } else {
1270
+ if( wpfval( $field, 'minLength' ) ) {
1271
+ if( wpforo_strlen( $value ) < $field['minLength'] ) {
1272
+ $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field length must be at least %d characters', false, false ), intval( $field['minLength'] ) );
1273
+ }
1274
+ }
1275
+ if( wpfval( $field, 'maxLength' ) ) {
1276
+ if( wpforo_strlen( $value ) > $field['maxLength'] ) {
1277
+ $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field length cannot be greater than %d characters', false, false ), intval( $field['maxLength'] ) );
1278
+ }
1279
+ }
1280
+ }
1281
+ if( $type === 'url' && filter_var( $value, FILTER_VALIDATE_URL ) === false ) {
1282
+ $error[] = $label . ' ' . wpforo_phrase( 'field value is not a valid URL', false, false );
1283
+ }
1284
+ if( $type === 'email' ) {
1285
+ if( ! is_email( $value ) ) {
1286
+ $error[] = $label . ' ' . wpforo_phrase( 'Invalid Email address', false, false );
1287
+ }
1288
+ if( $name === 'user_email' ) {
1289
+ $email_owner = email_exists( $value );
1290
+ if( $email_owner && $email_owner != $userid ) {
1291
+ $error[] = $label . ' ' . wpforo_phrase( 'This email address is already registered. Please insert another', false, false );
1292
+ }
1293
+ }
1294
+ }
1295
+ if( $type === 'file' ) {
1296
+ $extension = pathinfo( $value, PATHINFO_EXTENSION );
1297
+ $extension = ( function_exists( 'mb_strtolower' ) ) ? mb_strtolower( $extension ) : strtolower( $extension );
1298
+ if( wpfval( $field, 'fileExtensions' ) ) {
1299
+ if( $extension ) {
1300
+ if( ! in_array( $extension, $field['fileExtensions'] ) ) {
1301
+ $error[] = $label . ' ' . wpforo_phrase( 'file type is not allowed', false, false );
1302
+ $error[] = sprintf( 'Allowed file types: %s', implode( ', ', $field['fileExtensions'] ) );
1303
+ }
1304
+ } else {
1305
+ $error[] = $label . ' ' . wpforo_phrase( 'file type is not detected', false, false );
1306
+ }
1307
+ } else {
1308
+ $mime_types = get_allowed_mime_types();
1309
+ $mime_types = array_flip( $mime_types );
1310
+ if( ! empty( $mime_types ) ) {
1311
+ $implode_types = implode( '|', $mime_types );
1312
+ $explode_types = explode( '|', $implode_types );
1313
+ if( ! in_array( $extension, $explode_types ) ) {
1314
+ $error[] = $label . ' ' . sprintf( wpforo_phrase( 'file type %s is not allowed', false, false ), $extension );
1315
+ }
1316
+ if( ! WPF()->perm->can_attach_file_type( $extension ) ) {
1317
+ $error[] = 'You are not allowed to attach this file type';
1318
+ }
1319
+ }
1320
+ }
1321
+ if( wpfval( $field, 'fileSize' ) ) {
1322
+ if( wpfval( $_FILES, 'data', 'size', $name ) && $_FILES['data']['size'][ $name ] > ( $field['fileSize'] * 1024 * 1024 ) ) {
1323
+ $error[] = $label . ' ' . wpforo_phrase( 'file is too large', false, false );
1324
+ $error[] = sprintf( 'Maximum allowed file size is %s MB', $field['fileSize'] );
1325
+ }
1326
+ }
1327
+ }
1328
+ if( $name === 'user_nicename' ) {
1329
+ $value = sanitize_text_field( $value );
1330
+ $user_nicename = sanitize_title( sanitize_user( $value, true ) );
1331
+ if( ! $user_nicename ) {
1332
+ $error[] = 'Nickname validation failed';
1333
+ }
1334
+ $sql = "SELECT `ID` FROM `" . WPF()->db->users . "` WHERE `ID` != " . intval( $userid ) . " AND `user_nicename` LIKE '" . esc_sql( $user_nicename ) . "'";
1335
+ if( WPF()->db->get_var( $sql ) ) {
1336
+ $error[] = 'This nickname is already in use. Please insert another.';
1337
+ }
1338
+ }
1339
+ if( $name === 'groupid' ) {
1340
+ if( $template !== 'register' ) {
1341
+ if( $is_owner || ! wpforo_current_user_is( 'admin' ) ) {
1342
+ $error[] = 'You have no permission to edit Usergroup field';
1343
+ }
1344
+ } else {
1345
+ if( in_array( $value, [ 1, 2, 4 ] ) ) {
1346
+ $error[] = 'Admin and Moderator Usergroups are not permitted';
1347
+ } else {
1348
+ if( wpfval( $field, 'allowedGroupIds' ) ) {
1349
+ $allowedGroupIds = wpforo_parse_args( $field['allowedGroupIds'] );
1350
+ if( ! in_array( $value, $allowedGroupIds ) ) {
1351
+ $error[] = 'The selected Usergroup cannot be set';
1352
+ }
1353
+ } else {
1354
+ $error[] = 'The selected Usergroup is not found in allowed list';
1355
+ }
1356
+ }
1357
+ }
1358
+ }
1359
+ if( $name === 'secondary_groupids' ) {
1360
+ if( $template === 'register' || ( $is_owner && $field['isEditable'] ) || ( ! $is_owner && in_array( WPF()->current_user_groupid, (array) $field['canEdit'] ) ) || wpforo_current_user_is( 'admin' ) ) {
1361
+ if( ! empty( $value ) && is_array( $value ) ) {
1362
+ $secondary_groupids = WPF()->usergroup->get_secondary_groupids();
1363
+ foreach( $value as $secondary_groupid ) {
1364
+ if( in_array( $secondary_groupid, [ 1, 2, 4 ] ) ) {
1365
+ $error[] = 'Admin and Moderator Usergroups are not permitted';
1366
+ }
1367
+ if( $secondary_groupid && ! in_array( $secondary_groupid, $secondary_groupids ) ) {
1368
+ $error[] = 'One of the selected Usergroups cannot be set as Secondary';
1369
+ }
1370
+ }
1371
+ }
1372
+ } else {
1373
+ $error[] = 'You have no permission to edit Usergroup field';
1374
+ }
1375
+ }
1376
+ }
1377
+ }
1378
+ }
1379
+ }
1380
+ }
1381
+ }
1382
+ }
1383
+ if( ! empty( $error ) ) {
1384
+ $return['error'] = $error;
1385
+
1386
+ return $return;
1387
+ } else {
1388
+ return true;
1389
+ }
1390
+ }
1391
+
1392
+ public function validate_password( $data ) {
1393
+ $error = [];
1394
+ $return = [ 'error' => false ];
1395
+ if( wpfval( $data, 'old_pass' ) && wpfval( $data, 'user_pass1' ) && wpfval( $data, 'user_pass2' ) ) {
1396
+ if( $data['user_pass1'] !== $data['user_pass2'] ) {
1397
+ $error[] = 'New Passwords do not match';
1398
+ } else {
1399
+ return true;
1400
+ }
1401
+ }
1402
+ if( ! empty( $error ) ) {
1403
+ $return['error'] = $error;
1404
+
1405
+ return $return;
1406
+ }
1407
+
1408
+ return false;
1409
+ }
1410
+
1411
+ public function field_types( $fields ) {
1412
+ $name_type_fields = [];
1413
+ if( ! empty( $fields ) ) {
1414
+ foreach( $fields as $rows ) {
1415
+ foreach( $rows as $cols ) {
1416
+ foreach( $cols as $field ) {
1417
+ if( wpfval( $field, 'name' ) && wpfval( $field, 'type' ) ) {
1418
+ $name_type_fields[ $field['name'] ] = $field['type'];
1419
+ }
1420
+ }
1421
+ }
1422
+ }
1423
+ }
1424
+
1425
+ return $name_type_fields;
1426
+ }
1427
+
1428
+ public function owner( $object_userid = false ) {
1429
+ if( ! $object_userid ) {
1430
+ if( wpfval( WPF()->current_object, 'user', 'userid' ) ) {
1431
+ return wpforo_is_owner( WPF()->current_object['user']['userid'] );
1432
+ } else {
1433
+ return false;
1434
+ }
1435
+ } else {
1436
+ return wpforo_is_owner( $object_userid );
1437
+ }
1438
+ }
1439
+
1440
+ /**
1441
+ * @param array $f
1442
+ *
1443
+ * @return bool
1444
+ */
1445
+ public function can_add( $f ) {
1446
+ if( wpfval( $f, 'name' ) ) {
1447
+ if( $f['name'] === 'signature' && ! wpforo_setting( 'profiles', 'signature' ) ) {
1448
+ return false;
1449
+ }
1450
+ if( $f['name'] === 'avatar' && ( ! wpforo_setting( 'profiles', 'custom_avatars' ) || ! wpforo_setting( 'profiles', 'avatars' ) ) ) {
1451
+ return false;
1452
+ }
1453
+ }
1454
+
1455
+ return true;
1456
+ }
1457
+
1458
+ /**
1459
+ * @param array $f
1460
+ *
1461
+ * @return bool
1462
+ */
1463
+ public function can_view( $f ) {
1464
+ return ! ( ! $this->owner() && ! in_array( WPF()->current_user_groupid, $f['canView'] ) );
1465
+ }
1466
+
1467
+ /**
1468
+ * @param array $f
1469
+ *
1470
+ * @return bool
1471
+ */
1472
+ public function can_edit( $f ) {
1473
+ if( wpfval( WPF()->current_object, 'user', 'userid' ) ) {
1474
+ $is_owner = $this->owner();
1475
+ $value = wpfkey( $f, 'value' ) ? $f['value'] : false;
1476
+ $can_edit = wpfkey( $f, 'isEditable' ) ? $f['isEditable'] : false;
1477
+ $can_moderate = $this->can_moderate( $f );
1478
+ if( ! $is_owner && ! $can_moderate && WPF()->current_user_groupid !== 1 ) {
1479
+ return false;
1480
+ }
1481
+ if( ! $can_edit && ! $can_moderate && WPF()->current_user_groupid !== 1 && ! $value ) {
1482
+ return false;
1483
+ }
1484
+ if( wpfkey( $f, 'name' ) ) {
1485
+ if( $f['name'] === 'signature' && ( ! WPF()->usergroup->can( 'ups' ) || ! wpforo_setting( 'profiles', 'signature' ) ) ) {
1486
+ return false;
1487
+ }
1488
+ if( $f['name'] === 'avatar' && ( ! wpforo_setting( 'profiles', 'custom_avatars' ) || ! wpforo_setting( 'profiles', 'avatars' ) ) ) {
1489
+ return false;
1490
+ }
1491
+ if( $f['name'] === 'groupid' && ( WPF()->current_user_groupid != 1 || $is_owner || ! current_user_can( 'administrator' ) ) ) {
1492
+ return false;
1493
+ }
1494
+ }
1495
+
1496
+ return true;
1497
+ } else {
1498
+ return false;
1499
+ }
1500
+ }
1501
+
1502
+ /**
1503
+ * @param array $f
1504
+ *
1505
+ * @return bool
1506
+ */
1507
+ public function can_moderate( $f ) {
1508
+ if( empty( $f ) ) return false;
1509
+ $usergroups_who_can_edit = ! empty( $f['canEdit'] ) ? (array) $f['canEdit'] : [ 1 ];
1510
+
1511
+ return in_array( WPF()->current_user_groupid, $usergroups_who_can_edit );
1512
+ }
1513
+
1514
+ public function can_show_value( $f ) {
1515
+ $return = true;
1516
+ if( is_string( $f ) ) $f = WPF()->member->get_field( $f );
1517
+ $f = wpforo_parse_args( $f, $this->default );
1518
+ if( $f['type'] === 'password' ) $return = false;
1519
+
1520
+ return apply_filters( 'wpforo_form_can_show_value', $return, $f );
1521
+ }
1522
+ }
classes/Forums.php ADDED
@@ -0,0 +1,1260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ use stdClass;
6
+
7
+ // Exit if accessed directly
8
+ if( ! defined( 'ABSPATH' ) ) exit;
9
+
10
+ class Forums {
11
+ static $cache = [ 'forums' => [], 'forum' => [], 'item' => [] ];
12
+
13
+ public static function _get_forumids( $table, $layout = null ) {
14
+ $sql = "SELECT `forumid` FROM `$table` WHERE `status` = 1";
15
+ if( !is_null( $layout ) ) $sql .= WPF()->db->prepare( " AND `layout` = %d", $layout );
16
+ return WPF()->db->get_col($sql);
17
+ }
18
+
19
+ public static function get_forumids( $table, $layout = null ) {
20
+ return wpforo_ram_get( [self::class, '_get_forumids'], $table, $layout );
21
+ }
22
+
23
+ public function __construct() {
24
+ $this->init_hooks();
25
+ }
26
+
27
+ public function reset() {
28
+ self::$cache = [ 'forums' => [], 'forum' => [], 'item' => [] ];
29
+ }
30
+
31
+ private function init_hooks() {
32
+ add_action( 'wpforo_after_add_usergroup', [$this, 'after_add_usergroup'] );
33
+
34
+ add_action( 'wpforo_after_merge_forum', [$this, 'after_merge_forum'], 10, 2 );
35
+
36
+ add_action( 'wpforo_after_add_topic', [$this, 'after_add_topic'] );
37
+ add_action( 'wpforo_after_delete_topic', [$this, 'after_delete_topic'] );
38
+ add_action( 'wpforo_topic_status_update', [$this, 'after_topic_status_update'] );
39
+ add_action( 'wpforo_topic_private_update', [$this, 'topic_private_update'] );
40
+ add_action( 'wpforo_after_merge_topic', [$this, 'after_merge_topic'], 10, 2 );
41
+ add_action( 'wpforo_after_move_topic', [$this, 'after_move_topic'], 10, 2 );
42
+
43
+ add_action( 'wpforo_after_add_post', [$this, 'after_add_post'], 10, 3 );
44
+ add_action( 'wpforo_after_delete_post', [$this, 'after_delete_post'] );
45
+ add_action( 'wpforo_post_status_update', [$this, 'after_post_status_update'] );
46
+ }
47
+
48
+ public function get_cache( $var ) {
49
+ if( isset( self::$cache[ $var ] ) ) return self::$cache[ $var ];
50
+ }
51
+
52
+ private function unique_slug( $slug, $parentid = 0, $forumid = 0 ) {
53
+ $new_slug = wpforo_text( $slug, 250, false );
54
+ $forumid = intval( $forumid );
55
+ $i = 2;
56
+ while( ! WPF()->can_use_this_slug( $new_slug ) || WPF()->db->get_var( "SELECT `forumid` FROM " . WPF()->tables->forums . " WHERE `slug` = '" . esc_sql( $new_slug ) . "'" . ( $forumid ? ' AND `forumid` != ' . intval( $forumid ) : '' ) ) ) {
57
+ if( ! isset( $parent_slug ) && $parentid = intval( $parentid ) ) {
58
+ $parent_slug = WPF()->db->get_var( "SELECT `slug` FROM " . WPF()->tables->forums . " WHERE `forumid` = " . intval( $parentid ) );
59
+ $new_slug = $parent_slug . "-" . wpforo_text( $slug, 250, false );
60
+ } else {
61
+ $new_slug = wpforo_text( $slug, 250, false ) . '-' . $i ++;
62
+ }
63
+ }
64
+
65
+ return $new_slug;
66
+ }
67
+
68
+ public function add( $args = [], $checkperm = true ) {
69
+ if( $checkperm && ! WPF()->usergroup->can_manage_forum() ) {
70
+ WPF()->notice->add( 'Permission denied for add forum', 'error' );
71
+
72
+ return false;
73
+ }
74
+
75
+ if( empty( $args ) && empty( $_REQUEST['forum'] ) ) return false;
76
+ if( empty( $args ) && ! empty( $_REQUEST['forum'] ) ) $args = $_REQUEST['forum'];
77
+
78
+ $args['title'] = sanitize_text_field( stripslashes( wpfval( $args, 'title' ) ) );
79
+ if( ! $args['title'] ) {
80
+ WPF()->notice->add( 'Please insert required fields!', 'error' );
81
+
82
+ return false;
83
+ }
84
+
85
+ $args['title'] = wpforo_text( $args['title'], 250, false );
86
+ $args['parentid'] = (int) wpfval( $args, 'parentid' );
87
+
88
+ $args['slug'] = ( $ss = trim( sanitize_title( wpfval( $args, 'slug' ) ) ) ) ? $ss : ( ( $st = trim( sanitize_title( $args['title'] ) ) ) ? $st : md5( time() ) );
89
+ $args['slug'] = $this->unique_slug( $args['slug'], $args['parentid'] );
90
+
91
+ $args['description'] = wpforo_kses( stripslashes( wpfval( $args, 'description' ) ) );
92
+
93
+ $group_access_relation = WPF()->usergroup->get_usergroup_access_relation();
94
+ $args['permission'] = ( (array) wpfval( $args, 'permission' ) ) + $group_access_relation;
95
+ $args['permission'] = serialize( array_map( 'sanitize_text_field', $args['permission'] ) );
96
+
97
+ $args['meta_key'] = sanitize_text_field( stripslashes( wpfval( $args, 'meta_key' ) ) );
98
+ $args['meta_desc'] = sanitize_text_field( stripslashes( wpfval( $args, 'meta_desc' ) ) );
99
+ $args['icon'] = sanitize_text_field( wpfval( $args, 'icon' ) );
100
+ $args['cover'] = wpforo_bigintval( wpfval( $args, 'cover' ) );
101
+ $args['cover_height'] = intval( wpfval( $args, 'cover_height' ) );
102
+ if( ! $args['cover_height'] ) $args['cover_height'] = 150;
103
+ $args['topics'] = (int) wpfval( $args, 'topics' );
104
+ $args['posts'] = (int) wpfval( $args, 'posts' );
105
+ $args['order'] = (int) wpfval( $args, 'order' );
106
+
107
+ $args['status'] = (int) wpfval( $args, 'status' );
108
+ if( ! $args['status'] ) $args['status'] = 1;
109
+
110
+ $args['color'] = sanitize_text_field( stripslashes( wpfval( $args, 'color' ) ) );
111
+ if( ! $args['color'] ) $args['color'] = '#888888';
112
+
113
+ $args['is_cat'] = (int) wpfval( $args, 'is_cat' );
114
+ if( ! $args['parentid'] ) $args['is_cat'] = 1;
115
+
116
+ $args['layout'] = (int) wpfval( $args, 'layout' );
117
+ if( $args['parentid'] ) {
118
+ $layout = (int) WPF()->db->get_var( "SELECT `layout` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . $args['parentid'] );
119
+ if( $layout ) $args['layout'] = $layout;
120
+ }
121
+ if( ! $args['layout'] ) $args['layout'] = 1;
122
+
123
+ $args = apply_filters( 'wpforo_before_add_forum', $args, $checkperm );
124
+ if( ! $args ) return false;
125
+
126
+ if( WPF()->db->insert( WPF()->tables->forums,
127
+ [
128
+ 'title' => $args['title'],
129
+ 'slug' => $args['slug'],
130
+ 'description' => $args['description'],
131
+ 'parentid' => $args['parentid'],
132
+ 'icon' => $args['icon'],
133
+ 'cover' => $args['cover'],
134
+ 'cover_height' => $args['cover_height'],
135
+ 'topics' => $args['topics'],
136
+ 'posts' => $args['posts'],
137
+ 'permissions' => $args['permission'],
138
+ 'meta_key' => $args['meta_key'],
139
+ 'meta_desc' => $args['meta_desc'],
140
+ 'status' => $args['status'],
141
+ 'is_cat' => $args['is_cat'],
142
+ 'layout' => $args['layout'],
143
+ 'order' => $args['order'],
144
+ 'color' => $args['color'],
145
+ ],
146
+ [ '%s', '%s', '%s', '%d', '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%d', '%d', '%d', '%d', '%s', ]
147
+ ) ) {
148
+ $args['forumid'] = WPF()->db->insert_id;
149
+
150
+ do_action( 'wpforo_after_add_forum', $args, $checkperm );
151
+
152
+ $this->delete_tree_cache();
153
+ wpforo_clean_cache();
154
+ WPF()->notice->add( 'Your forum successfully added', 'success' );
155
+
156
+ return $args['forumid'];
157
+ }
158
+
159
+ WPF()->notice->add( 'Can\'t add forum', 'error' );
160
+
161
+ return false;
162
+ }
163
+
164
+ public function edit( $args = [], $checkperm = true ) {
165
+ if( $checkperm && ! WPF()->usergroup->can_manage_forum() ) {
166
+ WPF()->notice->add( 'Permission denied for edit forum', 'error' );
167
+
168
+ return false;
169
+ }
170
+
171
+ if( empty( $args ) && empty( $_REQUEST['forum'] ) ) return false;
172
+ if( empty( $args ) && ! empty( $_REQUEST['forum'] ) ) $args = $_REQUEST['forum'];
173
+ if( ! isset( $args['forumid'] ) && isset( $_GET['id'] ) ) $args['forumid'] = $_GET['id'];
174
+
175
+ $args['forumid'] = (int) wpfval( $args, 'forumid' );
176
+ if( ! $forum = $this->get_forum( $args['forumid'] ) ) {
177
+ WPF()->notice->add( 'Forum update error', 'error' );
178
+
179
+ return false;
180
+ }
181
+
182
+ $args['title'] = sanitize_text_field( stripslashes( wpfval( $args, 'title' ) ) );
183
+ if( ! $args['title'] ) {
184
+ WPF()->notice->add( 'Please insert required fields!', 'error' );
185
+
186
+ return false;
187
+ }
188
+
189
+ $args['title'] = wpforo_text( $args['title'], 250, false );
190
+ $args['parentid'] = (int) wpfval( $args, 'parentid' );
191
+ if( $args['forumid'] === $args['parentid'] ) $args['parentid'] = (int) $forum['parentid'];
192
+
193
+ $args['slug'] = ( $ss = trim( sanitize_title( wpfval( $args, 'slug' ) ) ) ) ? $ss : ( ( $st = trim( sanitize_title( $args['title'] ) ) ) ? $st : md5( time() ) );
194
+ $args['slug'] = $this->unique_slug( $args['slug'], $args['parentid'], $args['forumid'] );
195
+
196
+ $args['description'] = wpforo_kses( stripslashes( wpfval( $args, 'description' ) ) );
197
+
198
+ $group_access_relation = WPF()->usergroup->get_usergroup_access_relation();
199
+ $args['permission'] = ( (array) wpfval( $args, 'permission' ) ) + $group_access_relation;
200
+ $args['permission'] = serialize( array_map( 'sanitize_text_field', $args['permission'] ) );
201
+
202
+ $args['meta_key'] = sanitize_text_field( stripslashes( wpfval( $args, 'meta_key' ) ) );
203
+ $args['meta_desc'] = sanitize_text_field( stripslashes( wpfval( $args, 'meta_desc' ) ) );
204
+ $args['icon'] = sanitize_text_field( wpfval( $args, 'icon' ) );
205
+ $args['cover'] = wpforo_bigintval( wpfval( $args, 'cover' ) );
206
+ $args['cover_height'] = intval( wpfval( $args, 'cover_height' ) );
207
+ if( ! $args['cover_height'] ) $args['cover_height'] = 150;
208
+ $args['topics'] = (int) wpfval( $args, 'topics' );
209
+ $args['posts'] = (int) wpfval( $args, 'posts' );
210
+ $args['order'] = (int) wpfval( $args, 'order' );
211
+
212
+ $args['status'] = (int) wpfval( $args, 'status' );
213
+ if( ! $args['status'] ) $args['status'] = 1;
214
+
215
+ $args['color'] = sanitize_text_field( stripslashes( wpfval( $args, 'color' ) ) );
216
+ if( ! $args['color'] ) $args['color'] = '#888888';
217
+
218
+ $args['is_cat'] = (int) wpfval( $args, 'is_cat' );
219
+ if( ! $args['parentid'] ) $args['is_cat'] = 1;
220
+
221
+ $args['layout'] = (int) wpfval( $args, 'layout' );
222
+ if( $args['parentid'] ) {
223
+ $layout = (int) WPF()->db->get_var( "SELECT `layout` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . $args['parentid'] );
224
+ if( $layout ) $args['layout'] = $layout;
225
+ }
226
+ if( ! $args['layout'] ) $args['layout'] = 1;
227
+
228
+ $args = apply_filters( 'wpforo_before_edit_forum', $args, $forum, $checkperm );
229
+ if( ! $args ) return false;
230
+
231
+ if( false !== WPF()->db->update( WPF()->tables->forums,
232
+ [
233
+ 'title' => $args['title'],
234
+ 'slug' => $args['slug'],
235
+ 'description' => $args['description'],
236
+ 'parentid' => $args['parentid'],
237
+ 'icon' => $args['icon'],
238
+ 'cover' => $args['cover'],
239
+ 'cover_height' => $args['cover_height'],
240
+ 'permissions' => $args['permission'],
241
+ 'meta_key' => $args['meta_key'],
242
+ 'meta_desc' => $args['meta_desc'],
243
+ 'status' => $args['status'],
244
+ 'is_cat' => $args['is_cat'],
245
+ 'layout' => $args['layout'],
246
+ 'color' => $args['color'],
247
+ ],
248
+ [ 'forumid' => $args['forumid'] ],
249
+ [ '%s', '%s', '%s', '%d', '%s', '%d', '%d', '%s', '%s', '%s', '%d', '%d', '%d', '%s' ],
250
+ [ '%d' ] ) ) {
251
+ if( $childs = $this->get_childs( $args['forumid'] ) ) {
252
+ $sql = "UPDATE `" . WPF()->tables->forums . "` SET `layout` = " . $args['layout'] . " WHERE `forumid` IN(" . implode( ',', $childs ) . ")";
253
+ WPF()->db->query( $sql );
254
+ }
255
+
256
+ do_action( 'wpforo_after_edit_forum', $args, $forum, $checkperm );
257
+
258
+ $this->delete_tree_cache();
259
+ wpforo_clean_cache();
260
+ WPF()->notice->add( 'Forum successfully updated', 'success' );
261
+
262
+ return $args['forumid'];
263
+ }
264
+
265
+ WPF()->notice->add( 'Forum update error', 'error' );
266
+
267
+ return false;
268
+ }
269
+
270
+ public function delete( $forumid = 0, $checkperm = true ) {
271
+ $forumid = intval( $forumid );
272
+ if( ! $forumid && isset( $_REQUEST['id'] ) ) $forumid = intval( $_REQUEST['id'] );
273
+
274
+ if( $checkperm && ! WPF()->usergroup->can_manage_forum() ) {
275
+ WPF()->notice->add( 'Permission denied for delete forum', 'error' );
276
+
277
+ return false;
278
+ }
279
+
280
+ $forumids = $this->get_childs( $forumid );
281
+ $forumids[] = $forumid;
282
+ foreach( $forumids as $forumid ) $this->__delete( $forumid );
283
+
284
+ $this->delete_tree_cache();
285
+ wpforo_clean_cache();
286
+ WPF()->notice->add( 'Your forum successfully deleted', 'success' );
287
+
288
+ return true;
289
+ }
290
+
291
+ private function __delete( $forumid ) {
292
+ $forumid = intval( $forumid );
293
+ // START delete topic posts include first post
294
+ if( $topicids = WPF()->db->get_col( "SELECT `topicid` FROM " . WPF()->tables->topics . " WHERE `forumid` = " . $forumid ) ) {
295
+ foreach( $topicids as $topicid ) {
296
+ WPF()->topic->delete( $topicid, false, false );
297
+ }
298
+ }
299
+ // END delete topic posts include first post
300
+
301
+ WPF()->db->delete( WPF()->tables->forums, [ 'forumid' => $forumid ], [ '%d' ] );
302
+
303
+ do_action( 'wpforo_after_delete_forum', $forumid );
304
+ }
305
+
306
+ public function merge( $forumid = 0, $mergeid = 0 ) {
307
+ $forumid = intval( $forumid );
308
+ $mergeid = intval( $mergeid );
309
+
310
+ if( ! $forumid && isset( $_REQUEST['id'] ) ) $forumid = intval( $_REQUEST['id'] );
311
+ if( ! $mergeid && isset( $_REQUEST['forum']['mergeid'] ) ) $mergeid = intval( $_REQUEST['forum']['mergeid'] );
312
+
313
+ if( ! $forumid || ! $mergeid ) return false;
314
+
315
+ if( $child_forumids = $this->get_child_forums( $forumid ) ) {
316
+ $forumids = implode( ',', $child_forumids );
317
+ $merge_layout = $this->get_layout( $mergeid );
318
+
319
+ if( ! WPF()->db->query( "UPDATE " . WPF()->tables->forums . " SET `parentid` = " . $mergeid . ", `layout` = " . $merge_layout . " WHERE `forumid` IN(" . $forumids . ")" ) ) {
320
+ WPF()->notice->add( 'Forum merging error', 'error' );
321
+
322
+ return false;
323
+ }
324
+
325
+ if( $childs = $this->get_childs( $forumid ) ){
326
+ WPF()->db->query( "UPDATE " . WPF()->tables->forums . " SET `layout` = " . $merge_layout . " WHERE `forumid` IN(" . implode( ',', $childs ) . ")" );
327
+ }
328
+ }
329
+
330
+ WPF()->db->update( WPF()->tables->topics, [ 'forumid' => $mergeid ], [ 'forumid' => $forumid ], [ '%d' ], [ '%d' ] );
331
+ WPF()->db->update( WPF()->tables->posts, [ 'forumid' => $mergeid ], [ 'forumid' => $forumid ], [ '%d' ], [ '%d' ] );
332
+
333
+ if( WPF()->db->delete( WPF()->tables->forums, [ 'forumid' => $forumid ], [ '%d' ] ) ) {
334
+
335
+ do_action( 'wpforo_after_merge_forum', $forumid, $mergeid );
336
+
337
+ $this->delete_tree_cache();
338
+ wpforo_clean_cache( 'forum' );
339
+ WPF()->notice->add( 'Forum is successfully merged', 'success' );
340
+ return true;
341
+ }
342
+
343
+ WPF()->notice->add( 'Forum merging error', 'error' );
344
+
345
+ return false;
346
+ }
347
+
348
+ public function rebuild_last_infos( $forumid ) {
349
+ if( ! $forumid = intval( $forumid ) ) return;
350
+
351
+ $last_topicid = 0;
352
+ $last_postid = 0;
353
+ $last_userid = 0;
354
+ $last_post_date = '0000-00-00 00:00:00';
355
+
356
+ if( $last_topics = WPF()->topic->get_topics( [
357
+ 'forumid' => $forumid,
358
+ 'status' => 0,
359
+ 'private' => 0,
360
+ 'orderby' => 'topicid',
361
+ 'order' => 'DESC',
362
+ 'row_count' => 1,
363
+ ] ) ) {
364
+ $last_topic = $last_topics[0];
365
+ $last_topicid = $last_topic['topicid'];
366
+ }
367
+
368
+ $sql = "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `status` = 0 AND `private` = 0 AND `forumid` = %d ORDER BY `created` DESC, `postid` DESC LIMIT 1";
369
+ if( $last_postid = WPF()->db->get_var( WPF()->db->prepare( $sql, $forumid ) ) ) {
370
+ if( $last_post_data = WPF()->post->get_post( $last_postid ) ) {
371
+ $last_postid = $last_post_data['postid'];
372
+ $last_userid = $last_post_data['userid'];
373
+ $last_post_date = $last_post_data['created'];
374
+ }
375
+ } else {
376
+ $last_postid = 0;
377
+ }
378
+
379
+ $parent_ids = [];
380
+ $this->get_parents( $forumid, $parent_ids );
381
+ $parent_ids = array_unique( array_filter( array_map( 'wpforo_bigintval', (array) $parent_ids ) ) );
382
+
383
+ if( $parent_ids ) {
384
+ $sql = "UPDATE `" . WPF()->tables->forums . "`
385
+ SET `last_topicid` = %d,
386
+ `last_postid` = %d,
387
+ `last_userid` = %d,
388
+ `last_post_date` = %s
389
+ WHERE `forumid` IN(" . implode( ',', $parent_ids ) . ")";
390
+ WPF()->db->query( WPF()->db->prepare( $sql, $last_topicid, $last_postid, $last_userid, $last_post_date ) );
391
+ wpforo_clean_cache( 'forum' );
392
+ }
393
+ }
394
+
395
+ public function rebuild_stats( $forumid ) {
396
+ if( ! $forumid = intval( $forumid ) ) return false;
397
+ $topics = WPF()->topic->get_count( [ 'forumid' => $forumid, 'status' => 0, 'private' => 0 ] );
398
+ $posts = WPF()->post->get_count( [ 'forumid' => $forumid, 'status' => 0, 'private' => 0 ] );
399
+
400
+ if( false !== WPF()->db->update( WPF()->tables->forums, [ 'topics' => $topics, 'posts' => $posts ], [ 'forumid' => $forumid ], [ '%d', '%d' ], [ '%d' ] ) ) {
401
+ wpforo_clean_cache( 'forum' );
402
+
403
+ return true;
404
+ }
405
+
406
+ return false;
407
+ }
408
+
409
+ function _get_forum( $args ) {
410
+ $forum = [];
411
+ if( ! $args ) return $forum;
412
+
413
+ $default = [
414
+ 'forumid' => null,
415
+ 'slug' => '',
416
+ 'status' => null,
417
+ 'type' => 'all',
418
+ ];
419
+ if( ! is_array( $args ) ) {
420
+ if( is_numeric( $args ) ) {
421
+ $default['forumid'] = intval( $args );
422
+ if( $default['forumid'] === WPF()->current_object['forumid'] ) return WPF()->current_object['forum'];
423
+ } elseif( is_string( $args ) ) {
424
+ $default['slug'] = trim( $args );
425
+ if( $default['slug'] && $default['slug'] === wpfval( WPF()->current_object['forum'], 'slug' ) ) return WPF()->current_object['forum'];
426
+ }
427
+ }
428
+ $args = wpforo_parse_args( $args, $default );
429
+
430
+ $wheres = [];
431
+ if( $args['forumid'] ) $wheres[] = "`forumid` = " . intval( $args['forumid'] );
432
+ if( $args['slug'] ) $wheres[] = "`slug` = '" . esc_sql( $args['slug'] ) . "'";
433
+ if( ! is_null( $args['status'] ) ) $wheres[] = "`status` = " . intval( $args['status'] );
434
+ switch( $args['type'] ) {
435
+ case 'category':
436
+ $wheres[] = "`is_cat` = 1";
437
+ break;
438
+ case 'forum':
439
+ $wheres[] = "`is_cat` = 0";
440
+ break;
441
+ }
442
+
443
+ if( $wheres ) {
444
+ $sql = "SELECT * FROM `" . WPF()->tables->forums . "` WHERE " . implode( " AND ", $wheres );
445
+ if( $forum = WPF()->db->get_row( $sql, ARRAY_A ) ) {
446
+ if( ! $forum['layout'] ) $forum['layout'] = 1;
447
+ $forum['url'] = $this->get_forum_url( $forum );
448
+ if( $forum['cover'] = wpforo_bigintval( wpfval($forum, 'cover') ) ){
449
+ $image = wp_get_attachment_image_src( $forum['cover'], 'full' );
450
+ $forum['cover_url'] = (string) wpfval($image, 0);
451
+ }else{
452
+ $forum['cover_url'] = '';
453
+ }
454
+ }
455
+ }
456
+
457
+ return apply_filters( 'wpforo_get_forum', $forum, $args );
458
+ }
459
+
460
+ public function get_forum( $args ) {
461
+ return wpforo_ram_get( [ $this, '_get_forum' ], $args );
462
+ }
463
+
464
+ function get_forums( $args = [], &$items_count = 0, $count = false ) {
465
+ $cache = WPF()->cache->on();
466
+
467
+ $default = [
468
+ 'include' => [], // array( 2, 10, 25 )
469
+ 'exclude' => [], // array( 2, 10, 25 )
470
+ 'parent_include' => [], // array( 2, 10, 25 )
471
+ 'parent_exclude' => [], // array( 2, 10, 25 )
472
+ 'parentid' => null,
473
+ 'parent_slug' => '',
474
+ 'status' => null,
475
+ 'type' => 'all', // category, forum
476
+ 'orderby' => 'order', // order by `field`
477
+ 'order' => 'ASC', // ASC DESC
478
+ 'offset' => null, // OFFSET
479
+ 'row_count' => null, // ROW COUNT
480
+ 'layout' => null, // 1, 2, 3, 4
481
+ ];
482
+
483
+ $args = wpforo_parse_args( $args, $default );
484
+ extract( $args, EXTR_OVERWRITE );
485
+
486
+ $include = wpforo_parse_args( $include );
487
+ $exclude = wpforo_parse_args( $exclude );
488
+ $parent_include = wpforo_parse_args( $parent_include );
489
+ $parent_exclude = wpforo_parse_args( $parent_exclude );
490
+
491
+ $sql = "SELECT * FROM `" . WPF()->tables->forums . "`";
492
+ $wheres = [];
493
+
494
+ if( ! empty( $include ) ) $wheres[] = "`forumid` IN(" . implode( ', ', array_map( 'intval', $include ) ) . ")";
495
+ if( ! empty( $exclude ) ) $wheres[] = "`forumid` NOT IN(" . implode( ', ', array_map( 'intval', $exclude ) ) . ")";
496
+ if( ! empty( $parent_include ) ) $wheres[] = "`parentid` IN(" . implode( ', ', array_map( 'intval', $parent_include ) ) . ")";
497
+ if( ! empty( $parent_exclude ) ) $wheres[] = "`parentid` NOT IN(" . implode( ', ', array_map( 'intval', $parent_exclude ) ) . ")";
498
+ if( $parentid != null ) $wheres[] = " `parentid` = " . intval( $parentid );
499
+ if( $layout != null ) $wheres[] = " `layout` = " . intval( $layout );
500
+ if( $status != null ) $wheres[] = " `status` = " . intval( $status );
501
+
502
+ if( $type === 'category' ) {
503
+ $wheres[] = " `is_cat` = 1";
504
+ } elseif( $type === 'forum' ) {
505
+ $wheres[] = " `is_cat` = 0";
506
+ }
507
+
508
+ if( $parent_slug != '' ) $wheres[] = "`slug` = '" . esc_sql( $parent_slug ) . "'";
509
+
510
+ if( ! empty( $wheres ) ) $sql .= " WHERE " . implode( " AND ", $wheres );
511
+
512
+ if( $count ) {
513
+ $item_count_sql = preg_replace( '#SELECT.+?FROM#isu', 'SELECT count(*) FROM', $sql );
514
+ // $item_count_sql = preg_replace('#ORDER.+$#is', '', $item_count_sql);
515
+ if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
516
+ }
517
+
518
+ $sql .= esc_sql( " ORDER BY `$orderby` " . $order );
519
+
520
+ if( $row_count != null ) {
521
+ if( $offset != null ) {
522
+ $sql .= esc_sql( " LIMIT $offset,$row_count" );
523
+ } else {
524
+ $sql .= esc_sql( " LIMIT $row_count" );
525
+ }
526
+ }
527
+
528
+ if( $cache ) {
529
+ $object_key = md5( $sql . WPF()->current_user_groupid );
530
+ $object_cache = WPF()->cache->get( $object_key );
531
+ if( ! empty( $object_cache ) ) {
532
+ $items_count = $object_cache['items_count'];
533
+
534
+ return $object_cache['items'];
535
+ }
536
+ }
537
+
538
+ $forums = WPF()->db->get_results( $sql, ARRAY_A );
539
+
540
+ $forums = array_map( function( $forum ){
541
+ if( $forum['cover'] = wpforo_bigintval( wpfval($forum, 'cover') ) ){
542
+ $image = wp_get_attachment_image_src( $forum['cover'], 'full' );
543
+ $forum['cover_url'] = (string) wpfval($image, 0);
544
+ }else{
545
+ $forum['cover_url'] = '';
546
+ }
547
+
548
+ return $forum;
549
+ }, $forums );
550
+
551
+ $forums = apply_filters( 'wpforo_get_forums', $forums, $args );
552
+
553
+ if( $cache && isset( $object_key ) && ! empty( $forums ) ) {
554
+ self::$cache['forums'][ $object_key ]['items'] = $forums;
555
+ self::$cache['forums'][ $object_key ]['items_count'] = $items_count;
556
+ }
557
+
558
+ return $forums;
559
+ }
560
+
561
+ function search( $needle, $fields = [] ) {
562
+
563
+ if( $needle ) {
564
+ $needle = sanitize_text_field( $needle );
565
+ if( empty( $fields ) ) {
566
+ $fields = [
567
+ 'title',
568
+ 'description',
569
+ 'meta_key',
570
+ 'meta_desc',
571
+ ];
572
+ }
573
+
574
+ $sql = "SELECT `forumid` FROM `" . WPF()->tables->forums . "`";
575
+ $wheres = [];
576
+
577
+ foreach( $fields as $field ) {
578
+ $wheres[] = "`" . esc_sql( $field ) . "` LIKE '%" . esc_sql( $needle ) . "%'";
579
+ }
580
+
581
+ $sql .= " WHERE " . implode( " OR ", $wheres );
582
+
583
+ return WPF()->db->get_col( $sql );
584
+ }
585
+
586
+ return [];
587
+ }
588
+
589
+ function update_hierarchy() {
590
+ if( is_array( $_REQUEST['forum'] ) && ! empty( $_REQUEST['forum'] ) ) {
591
+ $i = 0;
592
+ foreach( $_REQUEST['forum'] as $hierarchy ) {
593
+
594
+ extract( $hierarchy );
595
+
596
+ if( ! isset( $forumid ) || ! $forumid = intval( $forumid ) ) continue;
597
+
598
+ if( false !== WPF()->db->update( WPF()->tables->forums, [
599
+ 'parentid' => ( isset( $parentid ) ? intval( $parentid ) : 0 ),
600
+ 'order' => ( isset( $order ) ? intval( $order ) : 0 ),
601
+ ], [ 'forumid' => intval( $forumid ) ], [
602
+ '%d',
603
+ '%d',
604
+ ], [ '%d' ] ) ) {
605
+ $i ++;
606
+ }
607
+
608
+ if( isset( $parentid ) && $parentid = intval( $parentid ) ) {
609
+ $layout = WPF()->db->get_var( "SELECT `layout` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . intval( $parentid ) );
610
+ WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `layout` = " . intval( $layout ) . " WHERE `forumid` = " . intval( $forumid ) );
611
+ }
612
+
613
+ }
614
+
615
+ WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `is_cat` = 0" );
616
+ WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `is_cat` = 1 WHERE `parentid` = 0" );
617
+
618
+ if( $i ) {
619
+ $this->delete_tree_cache();
620
+ WPF()->notice->add( 'Forum hierarchy successfully updated', 'success' );
621
+ } else {
622
+ WPF()->notice->add( 'Cannot update forum hierarchy', 'error' );
623
+ }
624
+
625
+ }
626
+ }
627
+
628
+ /**
629
+ * @param int $forumid
630
+ *
631
+ * @return array array of child forumids
632
+ */
633
+ public function get_childs( $forumid ) {
634
+ $key = [ 'forums', 'get_childs', $forumid ];
635
+ if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
636
+
637
+ if( wpforo_is_db_mysql8() ) {
638
+ $forumids = $this->__get_childs_mysql8( $forumid );
639
+ } else {
640
+ $forumids = $this->__get_childs( $forumid );
641
+ }
642
+
643
+ WPF()->ram_cache->set( $key, $forumids );
644
+
645
+ return $forumids;
646
+ }
647
+
648
+ /**
649
+ * @param int $forumid
650
+ *
651
+ * @return array array of child forumids
652
+ */
653
+ private function __get_childs( $forumid ) {
654
+ if( $forumid = intval( $forumid ) ) {
655
+ $sql = "SELECT GROUP_CONCAT(
656
+ @id := (
657
+ SELECT GROUP_CONCAT(`forumid` ORDER BY `order` ASC)
658
+ FROM `" . WPF()->tables->forums . "`
659
+ WHERE FIND_IN_SET( `parentid`, @id )
660
+ )
661
+ ) AS forumids
662
+ FROM ( SELECT @id := %s ) vars
663
+ STRAIGHT_JOIN `" . WPF()->tables->forums . "`
664
+ WHERE @id IS NOT NULL";
665
+ $sql = WPF()->db->prepare( $sql, $forumid );
666
+
667
+ $forumids = explode( ',', (string) WPF()->db->get_var( $sql ) );
668
+
669
+ return array_values( array_unique( array_filter( array_map( 'intval', $forumids ) ) ) );
670
+ }
671
+
672
+ return [];
673
+ }
674
+
675
+ /**
676
+ * @param int $forumid
677
+ *
678
+ * @return array array of child forumids
679
+ */
680
+ private function __get_childs_mysql8( $forumid ) {
681
+ if( $forumid = intval( $forumid ) ) {
682
+ $sql = "WITH RECURSIVE `forum_path` AS (
683
+ SELECT `forumid`, `parentid`
684
+ FROM `" . WPF()->tables->forums . "`
685
+ WHERE `parentid` = %d
686
+ UNION
687
+ SELECT f.`forumid`, f.`parentid`
688
+ FROM `" . WPF()->tables->forums . "` AS f
689
+ INNER JOIN `forum_path` AS fp ON fp.`parentid` <> fp.`forumid` AND fp.`forumid` = f.`parentid`
690
+ ) SELECT * FROM `forum_path`";
691
+ $sql = WPF()->db->prepare( $sql, $forumid );
692
+
693
+ $forumids = WPF()->db->get_col( $sql );
694
+
695
+ return array_values( array_unique( array_filter( array_map( 'intval', $forumids ) ) ) );
696
+ }
697
+
698
+ return [];
699
+ }
700
+
701
+ // get forums tree for drop down menu
702
+
703
+ /**
704
+ * Returns depth for this item.
705
+ *
706
+ * @param int $forumid
707
+ *
708
+ * @param int $depth
709
+ *
710
+ * @since 1.0.0
711
+ *
712
+ */
713
+ function count_depth( $forumid, &$depth ) {
714
+ if( wpforo_is_db_mysql8() ) {
715
+ $depth = $this->__count_depth_mysql8( $forumid );
716
+
717
+ return;
718
+ }
719
+
720
+ $sql = "SELECT `parentid` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = %d AND `parentid` <> %d";
721
+ $sql = WPF()->db->prepare( $sql, intval( $forumid ), intval( $forumid ) );
722
+
723
+ if( WPF()->ram_cache->exists( $sql ) ) {
724
+ $parentid = WPF()->ram_cache->get( $sql );
725
+ } else {
726
+ $parentid = WPF()->db->get_var( $sql );
727
+ WPF()->ram_cache->set( $sql, $parentid );
728
+ }
729
+
730
+ if( $parentid ) {
731
+ $depth ++;
732
+ $this->count_depth( $parentid, $depth );
733
+ }
734
+ }
735
+
736
+ private function __count_depth_mysql8( $forumid ) {
737
+ $sql = "WITH RECURSIVE `forum_path` AS(
738
+ SELECT `forumid`, `parentid`, 0 AS `depth`
739
+ FROM `" . WPF()->tables->forums . "`
740
+ WHERE `forumid` = %d
741
+ UNION
742
+ SELECT f.`forumid`, f.`parentid`, `depth` + 1
743
+ FROM `" . WPF()->tables->forums . "` f
744
+ INNER JOIN `forum_path` fp ON fp.`parentid` <> fp.`forumid` AND fp.`parentid` = f.`forumid`
745
+ ) SELECT `depth` FROM `forum_path` ORDER BY `depth` DESC LIMIT 1";
746
+
747
+ return (int) WPF()->db->get_var( WPF()->db->prepare( $sql, intval( $forumid ) ) );
748
+ }
749
+
750
+ /**
751
+ * @param int $forumid
752
+ *
753
+ * @return array
754
+ */
755
+ function get_child_forums( $forumid ) {
756
+ $sql = "SELECT `forumid`
757
+ FROM `" . WPF()->tables->forums . "`
758
+ WHERE `parentid` = " . intval( $forumid ) . "
759
+ AND `forumid` <> " . intval( $forumid ) . "
760
+ ORDER BY `order`";
761
+
762
+ return array_map( 'intval', WPF()->db->get_col( $sql ) );
763
+ }
764
+
765
+ function forum_list( $forumids, $type = 'select_box', $selected = [], $cats = true, $disabled = [] ) {
766
+ static $old_depth;
767
+ $disabled = (array) $disabled;
768
+ $selected = (array) $selected;
769
+
770
+ foreach( $forumids as $forumid ) {
771
+ if( ! $forumid || ( ! wpforo_is_admin() && ! WPF()->perm->forum_can( 'vf', $forumid ) ) || ( wpforo_is_admin() && ! WPF()->usergroup->can_manage_forum() ) ) continue;
772
+ $depth = 0;
773
+ $this->count_depth( $forumid, $depth );
774
+ $forum = wpforo_forum( $forumid );
775
+ $name = wpfval($forum, 'title');
776
+ if( $type == 'select_box' ) { ?>
777
+ <option value="<?php echo intval( $forumid ) ?>" <?php echo( ( ! $cats && $depth == 0 || ( ! empty( $disabled ) && in_array( $forumid, $disabled ) ) ) ? ' disabled ' : '' );
778
+ echo( in_array( $forumid, $selected ) ? ' selected ' : '' ) ?> > <?php echo esc_html( str_repeat( '— ', $depth ) . trim( $name ) ) ?></option><?php
779
+ } elseif( $type === 'drag_menu' ) {
780
+ switch( $forum['layout'] ) {
781
+ case 2:
782
+ $layout_name = 'Simplified Layout';
783
+ break;
784
+ case 3:
785
+ $layout_name = 'Q&A Layout';
786
+ break;
787
+ case 4:
788
+ $layout_name = 'Threaded Layout';
789
+ break;
790
+ default:
791
+ $layout_name = 'Extended Layout';
792
+ }
793
+ ?>
794
+
795
+ <li id="menu-item-<?php echo intval( $forumid ) ?>" class="menu-item menu-item-depth-<?php echo esc_attr( $depth ) ?>">
796
+ <input id="forumid-<?php echo intval( $forumid ) ?>" type="hidden" name="forum[<?php echo intval( $forumid ) ?>][forumid]"/>
797
+ <input id="parentid-<?php echo intval( $forumid ) ?>" type="hidden" name="forum[<?php echo intval( $forumid ) ?>][parentid]"/>
798
+ <input id="order-<?php echo intval( $forumid ) ?>" type="hidden" name="forum[<?php echo intval( $forumid ) ?>][order]"/>
799
+ <dl class="menu-item-bar">
800
+ <dt class="menu-item-handle forum_width">
801
+ <span class="item-title forumtitle"><span style="font-weight:400; cursor:help;" title="Forum ID"><?php echo $forumid; ?> &nbsp;|&nbsp;</span> <?php echo apply_filters( 'wpforo_dashboard_forums_item_name', esc_html($name), $forumid); ?></span>
802
+ <span class="item-controls">
803
+ <span class="wpforo-cat-layout"><?php echo( $depth != 0 ? __( 'Topics', 'wpforo' ) . '&nbsp;(' . intval( $forum['topics'] ) . ')&nbsp;,&nbsp;' . __( 'Posts', 'wpforo' ) . '&nbsp;(' . intval( $forum['posts'] ) . ')&nbsp; | &nbsp;' : '' ) ?><?php echo( $depth == 0 ? '(&nbsp;<i>' . esc_html( $layout_name ) . '</i>&nbsp;)&nbsp; | &nbsp;' : '' ); ?></span>
804
+ <span class="menu_add">
805
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&action=add&parentid=' . intval( $forumid ) ) ?>">
806
+ <span class="dashicons dashicons-plus" title="<?php if( $depth ) : _e( 'Add a new Subforum', 'wpforo' ); else: _e( 'Add a new Forum in this Category', 'wpforo' ); endif; ?>"></span>
807
+ </a>
808
+ </span> &nbsp;|&nbsp;
809
+ <span class="menu_edit">
810
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&id=' . intval( $forumid ) . '&action=edit' ) ?>">
811
+ <span class="dashicons dashicons-edit" title="<?php _e( 'edit', 'wpforo' ) ?>"></span>
812
+ </a>
813
+ </span>&nbsp;|&nbsp;
814
+ <?php if( WPF()->usergroup->can_manage_forum() ): ?>
815
+ <span class="menu_delete">
816
+ <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'forums' ) . '&id=' . intval( $forumid ) . '&action=del' ) ?>">
817
+ <span class="dashicons dashicons-trash" title="<?php _e( 'delete', 'wpforo' ) ?>"></span>
818
+ </a>
819
+ </span>&nbsp;|&nbsp;
820
+ <?php endif; ?>
821
+ <span class="menu_view">
822
+ <a href="<?php echo esc_url( wpforo_forum( $forumid, 'url' ) ); ?>">
823
+ <span class="dashicons dashicons-visibility" title="<?php _e( 'View', 'wpforo' ) ?>"></span>
824
+ </a>
825
+ </span>
826
+
827
+ </span>
828
+ </dt>
829
+ </dl>
830
+ <ul class="menu-item-transport"></ul>
831
+ </li>
832
+
833
+ <?php
834
+ } elseif( $type === 'front_list' ) {
835
+ if( isset( $old_depth ) && $old_depth == $depth ) echo '</dd><dd>';
836
+ if( isset( $old_depth ) && $old_depth < $depth ) echo '<dl><dd>';
837
+ if( isset( $old_depth ) && $old_depth > $depth ) echo '</dd></dl>';
838
+ $old_depth = $depth;
839
+ if( isset( WPF()->current_object ) && isset( WPF()->current_object['forumid'] ) ) {
840
+ if( $forumid === intval( WPF()->current_object['forumid']) ) {
841
+ echo '<span class="wpf-dl-item wpf-dl-current"><span class="wpf-circle wpf-s" style="color: ' . $forum['color'] . '; display: inline-flex;"><i class="' . $forum['icon'] . '"></i></span><strong>' . esc_html( $name ) . '</strong></span>';
842
+ } else {
843
+ echo '<span class="wpf-dl-item"><a href="' . esc_url( wpforo_forum( $forumid, 'url' ) ) . '" ><span class="wpf-circle wpf-s" style="color: ' . $forum['color'] . '; display: inline-flex;"><i class="' . $forum['icon'] . '"></i></span>' . esc_html( $name ) . '</a></span>';
844
+ }
845
+ } else {
846
+ echo '<span class="wpf-dl-item"><a href="' . esc_url( wpforo_forum( $forumid, 'url' ) ) . '" ><span class="wpf-circle wpf-s" style="color: ' . $forum['color'] . '; display: inline-flex;"><i class="' . $forum['icon'] . '"></i></span>' . esc_html( $name ) . '</a></span>';
847
+ }
848
+ } elseif( $type === 'subscribe_manager_form' ) {
849
+ ?>
850
+ <li>
851
+ <?php if( $depth > 0 ) :
852
+ $forum_topic_attr = '';
853
+ $forum_attr = '';
854
+ if( key_exists( $forumid, $selected ) ) {
855
+ if( $selected[ $forumid ] == 'forum-topic' ) {
856
+ $forum_topic_attr = ' checked ';
857
+ } elseif( $selected[ $forumid ] == 'forum' ) {
858
+ $forum_attr = ' checked ';
859
+ }
860
+ }
861
+ ?>
862
+ <div class="wpf-sbs-div wpf-sbs-checkbox">
863
+ <input id="wpf_sbs_allposts_<?php echo $forumid ?>" type="checkbox" name="wpforo[forums][<?php echo $forumid ?>]" value="forum-topic" <?php echo $forum_topic_attr ?>><label class="wpf-sbsp" for="wpf_sbs_allposts_<?php echo $forumid ?>"><?php wpforo_phrase( 'topics and posts' ) ?></label>
864
+ <input id="wpf_sbs_alltopics_<?php echo $forumid ?>" type="checkbox" name="wpforo[forums][<?php echo $forumid ?>]" value="forum" <?php echo $forum_attr ?>><label class="wpf-sbst" for="wpf_sbs_alltopics_<?php echo $forumid ?>"><?php wpforo_phrase( 'topics' ) ?></label>
865
+ </div>
866
+ <?php endif; ?>
867
+ <div class="wpf-sbs-div wpf-sbs-form-title<?php echo ( $depth > 0 ) ? ' wpf-sbs-forum' : ' wpf-sbs-cat'; ?>"><?php echo esc_html( str_repeat( '— ', $depth ) ) . trim( $name ) ?></div>
868
+ </li>
869
+ <?php
870
+ }
871
+ $subforums = $this->get_child_forums( $forumid );
872
+ if( ! empty( $subforums ) ) {
873
+ $this->forum_list( $subforums, $type, $selected, true, $disabled );
874
+ }
875
+ }
876
+ }
877
+
878
+ function tree( $type = 'select_box', $cats = true, $selected = [], $cache = true, $disabled = [], $parentids = [] ) {
879
+ $disabled = (array) $disabled;
880
+ $selected = (array) $selected;
881
+ $parentids = (array) $parentids;
882
+ if( ! $parentids ) $parentids = WPF()->db->get_col( "SELECT `forumid` FROM `" . WPF()->tables->forums . "` WHERE `parentid` = 0 ORDER BY `order`" );
883
+ if( ! empty( $parentids ) ) {
884
+ if( $cache && ! wpforo_is_admin() ) {
885
+ $key = md5( serialize( $parentids ) . $type . (int) $cats . WPF()->current_user_groupid );
886
+ $html = wpforo_get_option( 'forum_tree_' . $key, '' );
887
+ $pattern_strip_selected = '#(<(?:option|input)[^<>]*?)[\r\n\t\s]*(?:selected|checked)[^\r\n\t\s]*?((?:[\r\n\t\s][^<>]*)?>)#isu';
888
+
889
+ if( $html ) {
890
+ if( $type === 'select_box' || $type === 'subscribe_manager_form' ) $html = preg_replace( $pattern_strip_selected, '$1$2', $html );
891
+ if( $selected ) {
892
+ if( $type === 'select_box' ) {
893
+ foreach( $selected as $sfid ) {
894
+ $html = str_replace( 'value="' . $sfid . '"', 'value="' . $sfid . '" selected ', $html );
895
+ }
896
+ } elseif( $type === 'subscribe_manager_form' ) {
897
+ foreach( $selected as $forumid => $stype ) {
898
+ $html = preg_replace( '#(name=[\'"]wpforo\[forums\]\[' . intval( $forumid ) . '\][\'"][^<>]*?value=[\'"]' . preg_quote( $stype ) . '[\'"]|value=[\'"]' . preg_quote( $stype ) . '[\'"][^<>]*?name=[\'"]wpforo\[forums\]\[' . intval( $forumid ) . '\][\'"])#isu', '$1 checked', $html );
899
+ }
900
+ }
901
+ }
902
+ echo $html;
903
+ } elseif( function_exists( 'ob_start' ) ) {
904
+ ob_start();
905
+ $this->forum_list( $parentids, $type, $selected, $cats, $disabled );
906
+ $html = ob_get_clean();
907
+ $cache_html = ( $type === 'select_box' ? preg_replace( $pattern_strip_selected, '$1$2', $html ) : $html );
908
+ if( $type !== 'drag_menu' ) wpforo_update_option( 'forum_tree_' . $key, $cache_html );
909
+ echo $html;
910
+ }
911
+ } else {
912
+ $this->forum_list( $parentids, $type, $selected, $cats, $disabled );
913
+ }
914
+ }
915
+ }
916
+
917
+ public function delete_tree_cache() {
918
+ WPF()->db->query( "DELETE FROM `" . WPF()->db->options . "` WHERE `option_name` REGEXP '^" . wpforo_prefix( 'forum_tree_' ) . "'" );
919
+ }
920
+
921
+ function parentid( $topicid = 0 ) {
922
+ if( isset( $_GET['page'] ) && preg_match( '#^wpforo-(?:\d+-)?forums$#iu', $_GET['page'] ) ) {
923
+ if( isset( $_GET['id'] ) ) return WPF()->db->get_var( "SELECT `parentid` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . intval( $_GET['id'] ) );
924
+ } elseif( isset( $_GET['page'] ) && preg_match( '#^wpforo-(?:\d+-)?topics$#iu', $_GET['page'] ) ) {
925
+ if( isset( $_GET['id'] ) ) return WPF()->db->get_var( "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = " . wpforo_bigintval( $_GET['id'] ) );
926
+ } else {
927
+ if( $topicid ) return WPF()->db->get_var( "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = " . wpforo_bigintval( $topicid ) );
928
+ }
929
+ }
930
+
931
+ function permissions() {
932
+ $access_arr = WPF()->perm->get_accesses();
933
+ if( ! empty( $access_arr ) ) {
934
+
935
+ if( isset( $_GET['id'] ) ) {
936
+ if( $permissions_srlz = WPF()->db->get_var( "SELECT `permissions` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = " . intval( $_GET['id'] ) ) ) {
937
+ $permissions_arr = unserialize( $permissions_srlz );
938
+ }
939
+ }
940
+
941
+ if( $usergroups = WPF()->db->get_results( "SELECT `groupid`, `name` FROM `" . WPF()->tables->usergroups . "`", ARRAY_A ) ) {
942
+ foreach( $usergroups as $usergroup ) {
943
+ extract( $usergroup, EXTR_OVERWRITE );
944
+ echo '
945
+ <tr>
946
+ <td>' . esc_html( $name ) . '</td>
947
+ <td>
948
+ <select name="forum[permission][' . intval( $groupid ) . ']">';
949
+ foreach( $access_arr as $value ) {
950
+
951
+ echo '<option value="' . esc_attr(
952
+ $value['access']
953
+ ) . '" ' . ( ( isset( $permissions_arr[ $groupid ] ) && $value['access'] == $permissions_arr[ $groupid ] ) || ( ! isset( $permissions_arr[ $groupid ] ) && ( ( $name == 'Guest' && $value['access'] == 'read_only' ) || ( $name == 'Registered' && $value['access'] == 'standard' ) || ( $name == 'Customer' && $value['access'] == 'standard' ) || ( $name == 'Moderator' && $value['access'] == 'moderator' ) || ( $name == 'Admin' && $value['access'] == 'full' ) || ( $name != 'Guest' && $name != 'Registered' && $name != 'Customer' && $name != 'Moderator' && $name != 'Admin' && $value['access'] == 'standard' ) ) ) ? ' selected ' : '' ) . '>' . esc_html(
954
+ __( $value['title'], 'wpforo' )
955
+ ) . '</option>';
956
+ }
957
+ echo '
958
+ </select>
959
+ </td>
960
+ </tr>
961
+ ';
962
+ }
963
+
964
+
965
+ }
966
+ }
967
+ }
968
+
969
+ /**
970
+ * array get_counts(array or id(num))
971
+ *
972
+ * @param array defined arguments array for returning
973
+ *
974
+ * @return array array('topics' => 0, 'posts' => 0)
975
+ * @since 1.0.0
976
+ *
977
+ */
978
+ function get_counts( $forumids ) {
979
+ $result = [ 'topics' => 0, 'posts' => 0 ];
980
+ $forumids = array_filter( array_map( 'intval', (array) $forumids ) );
981
+ if( $forumids ) {
982
+ $sql = "SELECT SUM(`topics`) as topics, SUM(`posts`) as posts FROM `" . WPF()->tables->forums . "` WHERE `forumid` IN(" . implode( ',', $forumids ) . ")";
983
+ $row = (array) WPF()->db->get_row( $sql, ARRAY_A );
984
+ $result['topics'] = (int) wpfval( $row, 'topics' );
985
+ $result['posts'] = (int) wpfval( $row, 'posts' );
986
+ }
987
+
988
+ return $result;
989
+ }
990
+
991
+ /**
992
+ * array get_layout(array)
993
+ *
994
+ * @param array defined arguments array for returning
995
+ *
996
+ * @return int layout id
997
+ * @since 1.0.0
998
+ *
999
+ */
1000
+ function get_layout( $args = null ) {
1001
+ if( is_null( $args ) ) $args = WPF()->current_object['forum'];
1002
+ if( ! $args ) return 1;
1003
+ if( $layout = (int) wpfval( $args, 'layout' ) ) return $layout;
1004
+ if( is_array( $args ) ) {
1005
+ $default = [
1006
+ 'forumid' => null, // forum id
1007
+ 'topicid' => null, // topic id
1008
+ 'postid' => null, // post id
1009
+ ];
1010
+ } else {
1011
+ $default = [
1012
+ 'forumid' => $args, // forumid
1013
+ 'topicid' => null, // topic id
1014
+ 'postid' => null, // post id
1015
+ ];
1016
+ }
1017
+ $args = wpforo_parse_args( $args, $default );
1018
+ if( ! empty( $args ) ) {
1019
+ extract( $args, EXTR_OVERWRITE );
1020
+
1021
+ if( $args['forumid'] ) {
1022
+ $layout = (int) wpforo_forum( $args['forumid'], 'layout' );
1023
+
1024
+ return ( $layout ?: 1 );
1025
+ } elseif( $args['topicid'] ) {
1026
+ $sql = "SELECT `forumid` FROM `" . WPF()->tables->topics . "` WHERE `topicid` = " . intval( $args['topicid'] );
1027
+ $forumid = WPF()->db->get_var( $sql );
1028
+
1029
+ return $this->get_layout( [ 'forumid' => $forumid ] );
1030
+ } elseif( $args['postid'] ) {
1031
+ $sql = "SELECT `forumid` FROM `" . WPF()->tables->posts . "` WHERE `postid` = " . intval( $args['postid'] );
1032
+ $forumid = WPF()->db->get_var( $sql );
1033
+
1034
+ return $this->get_layout( [ 'forumid' => $forumid ] );
1035
+ }
1036
+ }
1037
+
1038
+ return 1;
1039
+ }
1040
+
1041
+ function get_forum_url( $forum ) {
1042
+
1043
+ if( ! is_array( $forum ) ) {
1044
+ if( is_numeric( $forum ) ) {
1045
+ $forum = $this->get_forum( $forum );
1046
+ } else {
1047
+ $forum = [ 'slug' => $forum ];
1048
+ }
1049
+ }
1050
+
1051
+ if( is_array( $forum ) && ! empty( $forum ) ) {
1052
+ return wpforo_home_url( utf8_uri_encode( $forum['slug'] ) );
1053
+ } else {
1054
+ return wpforo_home_url();
1055
+ }
1056
+ }
1057
+
1058
+ function get_parents( $forumid, &$relative_ids ) {
1059
+ if( wpforo_is_db_mysql8() ) {
1060
+ $relative_ids = $this->__get_parents_mysql8( $forumid );
1061
+
1062
+ return;
1063
+ }
1064
+
1065
+ $sql = "SELECT `parentid`, `forumid` FROM `" . WPF()->tables->forums . "` WHERE `forumid` = %d";
1066
+ $sql = WPF()->db->prepare( $sql, $forumid );
1067
+ if( WPF()->ram_cache->exists( $sql ) ) {
1068
+ $forum = WPF()->ram_cache->get( $sql );
1069
+ } else {
1070
+ $forum = WPF()->db->get_row( $sql, ARRAY_A );
1071
+ WPF()->ram_cache->set( $sql, $forum );
1072
+ }
1073
+ if( $forum ) {
1074
+ $relative_ids[] = $forum['forumid'];
1075
+ if( $forum['parentid'] ) {
1076
+ $this->get_parents( $forum['parentid'], $relative_ids );
1077
+ } else {
1078
+ $relative_ids = array_reverse( $relative_ids );
1079
+ }
1080
+ }
1081
+ }
1082
+
1083
+ /**
1084
+ * @param int $forumid
1085
+ *
1086
+ * @return array
1087
+ */
1088
+ private function __get_parents_mysql8( $forumid ) {
1089
+ $sql = "WITH RECURSIVE `forum_path` AS(
1090
+ SELECT `forumid`, `parentid`, 0 AS `depth`
1091
+ FROM `" . WPF()->tables->forums . "`
1092
+ WHERE `forumid` = %d
1093
+ UNION
1094
+ SELECT f.`forumid`, f.`parentid`, `depth` + 1
1095
+ FROM `" . WPF()->tables->forums . "` f
1096
+ INNER JOIN `forum_path` fp ON fp.`parentid` <> fp.`forumid` AND fp.`parentid` = f.`forumid`
1097
+ ) SELECT `forumid` FROM `forum_path` ORDER BY `depth` DESC";
1098
+ $sql = WPF()->db->prepare( $sql, intval( $forumid ) );
1099
+ if( WPF()->ram_cache->exists( $sql ) ) {
1100
+ $relative_ids = WPF()->ram_cache->get( $sql );
1101
+ } else {
1102
+ $relative_ids = (array) WPF()->db->get_col( $sql );
1103
+ WPF()->ram_cache->set( $sql, $relative_ids );
1104
+ }
1105
+
1106
+ return $relative_ids;
1107
+ }
1108
+
1109
+ function get_count( $args = [] ) {
1110
+ $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->forums . "`";
1111
+ if( ! empty( $args ) ) {
1112
+ $wheres = [];
1113
+ foreach( $args as $key => $value ) $wheres[] = "`$key` = " . intval( $value );
1114
+ if( $wheres ) $sql .= " WHERE " . implode( ' AND ', $wheres );
1115
+ }
1116
+
1117
+ return WPF()->db->get_var( $sql );
1118
+ }
1119
+
1120
+ function get_lastinfo( $ids = [] ) {
1121
+ $lastinfo = [];
1122
+ if( ! empty( $ids ) ) {
1123
+ $ids = implode( ',', array_map( 'intval', $ids ) );
1124
+ $lastinfo = WPF()->db->get_row( "SELECT `userid` as last_userid, `topicid` as last_topicid, `postid` as last_postid, `created` as last_post_date FROM `" . WPF()->tables->posts . "` WHERE `status` = 0 AND `private` = 0 AND forumid IN(" . $ids . ") ORDER BY `created` DESC LIMIT 1", ARRAY_A );
1125
+ }
1126
+
1127
+ return $lastinfo;
1128
+ }
1129
+
1130
+ function forums() {
1131
+ $forums = $this->get_forums( [ 'parentid' => 0 ] );
1132
+
1133
+ return $this->children( $forums );
1134
+ }
1135
+
1136
+ function children( $forums, $parentId = 0, $level = 0 ) {
1137
+ if( empty( $forums ) || ! is_array( $forums ) ) return;
1138
+ $items = [];
1139
+ $level = $level + 1;
1140
+ foreach( $forums as $forum ) {
1141
+ if( ! isset( $forum['forumid'] ) || ! WPF()->perm->forum_can( 'vf', $forum['forumid'] ) ) continue;
1142
+ $forum['level'] = $level + 1;
1143
+ if( $forum['parentid'] == $parentId ) {
1144
+ $children = $this->children( $forums, $forum['forumid'], $level );
1145
+ if( $children ) {
1146
+ $forum['children'] = $children;
1147
+ }
1148
+ $items[] = $forum;
1149
+ }
1150
+ }
1151
+
1152
+ return $items;
1153
+ }
1154
+
1155
+ function dropdown( $forums = [] ) {
1156
+ if( empty( $forums ) ) {
1157
+ $forums = $this->forums();
1158
+ }
1159
+ foreach( $forums as $forum ) {
1160
+ if( isset( $forum['level'] ) ) $forum['level'] = $forum['level'] - 2;
1161
+ $prefix = ( $forum['level'] == 0 ) ? '' : str_repeat( '&mdash;', $forum['level'] );
1162
+ echo '<option value="' . esc_attr( $forum['forumid'] ) . '"> ' . $prefix . '&nbsp;' . esc_html( $forum['title'] ) . '</option>';
1163
+ if( ! empty( $forum['children'] ) ) {
1164
+ $this->dropdown( $forum['children'] );
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ function private_forum( $forumid, $groupids = [] ) {
1170
+ if( $forumid = intval( $forumid ) ) {
1171
+ if( ! $groupids ) $groupids = WPF()->current_user_groupids;
1172
+ if( ($groupids = array_map( 'intval', (array) $groupids)) && !WPF()->perm->forum_can( 'vf', $forumid, $groupids ) ) return true;
1173
+ }
1174
+
1175
+ return false;
1176
+ }
1177
+
1178
+ public function after_add_usergroup( $group ) {
1179
+ if( $group['groupid'] ) {
1180
+ if( $forums = $this->get_forums() ) {
1181
+ foreach( $forums as $forum ) {
1182
+ if( $permissions = (string) wpfval( $forum, 'permissions' ) ) {
1183
+ if( $permissions = maybe_unserialize( $permissions ) ) {
1184
+ $permissions[ $group['groupid'] ] = $group['access'];
1185
+ WPF()->db->update( WPF()->tables->forums, [ 'permissions' => serialize( $permissions ) ], [ 'forumid' => $forum['forumid'] ], [ '%s' ], [ '%d' ] );
1186
+ }
1187
+ }
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+
1193
+ public function after_merge_forum( $forumid, $mergeid ) {
1194
+ $this->rebuild_last_infos( $mergeid );
1195
+ $this->rebuild_stats( $mergeid );
1196
+ }
1197
+
1198
+ public function after_add_topic( $topic ) {
1199
+ if( !intval($topic['status']) && !intval($topic['private']) ){
1200
+ $this->rebuild_last_infos( $topic['forumid'] );
1201
+ $this->rebuild_stats( $topic['forumid'] );
1202
+ }
1203
+ }
1204
+
1205
+ public function after_delete_topic( $topic ) {
1206
+ if( !intval($topic['status']) && !intval($topic['private']) ){
1207
+ $this->rebuild_last_infos( $topic['forumid'] );
1208
+ $this->rebuild_stats( $topic['forumid'] );
1209
+ }
1210
+ }
1211
+
1212
+ public function after_topic_status_update( $topic ) {
1213
+ if( !intval($topic['private']) ){
1214
+ $this->rebuild_last_infos( $topic['forumid'] );
1215
+ $this->rebuild_stats( $topic['forumid'] );
1216
+ }
1217
+ }
1218
+
1219
+ public function topic_private_update( $topicid ) {
1220
+ if( $topic = WPF()->topic->get_topic( $topicid ) ){
1221
+ $this->rebuild_last_infos( $topic['forumid'] );
1222
+ $this->rebuild_stats( $topic['forumid'] );
1223
+ }
1224
+ }
1225
+
1226
+ public function after_merge_topic( $target, $current ) {
1227
+ $this->rebuild_last_infos( $target['forumid'] );
1228
+ $this->rebuild_stats( $target['forumid'] );
1229
+ $this->rebuild_last_infos( $current['forumid'] );
1230
+ $this->rebuild_stats( $current['forumid'] );
1231
+ }
1232
+
1233
+ public function after_move_topic( $topic, $forumid ) {
1234
+ $this->rebuild_last_infos( $topic['forumid'] );
1235
+ $this->rebuild_stats( $topic['forumid'] );
1236
+ $this->rebuild_last_infos( $forumid );
1237
+ $this->rebuild_stats( $forumid );
1238
+ }
1239
+
1240
+ public function after_add_post( $post, $topic, $forum ) {
1241
+ if( !intval( $post['status'] ) ){
1242
+ $this->rebuild_last_infos( $forum['forumid'] );
1243
+ $this->rebuild_stats( $forum['forumid'] );
1244
+ }
1245
+ }
1246
+
1247
+ public function after_delete_post( $post ) {
1248
+ if( !intval( $post['status'] ) ){
1249
+ $this->rebuild_last_infos( $post['forumid'] );
1250
+ $this->rebuild_stats( $post['forumid'] );
1251
+ }
1252
+ }
1253
+
1254
+ public function after_post_status_update( $post ) {
1255
+ if( !intval( $post['private'] ) ){
1256
+ $this->rebuild_last_infos( $post['forumid'] );
1257
+ $this->rebuild_stats( $post['forumid'] );
1258
+ }
1259
+ }
1260
+ }
classes/Logs.php ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ // Exit if accessed directly
6
+ if( ! defined( 'ABSPATH' ) ) exit;
7
+
8
+ class Logs {
9
+ public function read() {
10
+ $data = WPF()->current_object;
11
+
12
+ if( wpforo_setting( 'logging', 'view_logging' ) && wpfval( $data, 'template' ) ) {
13
+ if( $data['template'] === 'post' ) {
14
+ if( wpfval( $data, 'topicid' ) && wpfval( $data, 'topic', 'last_post' ) ) {
15
+ $this->read_item( $data['topicid'], $data['topic']['last_post'], wpforo_prefix( 'read_topics' ) );
16
+ }
17
+ if( wpfval( $data, 'forumid' ) ) {
18
+ $end_date = time() - ( 14 * 24 * 60 * 60 );
19
+ $args = [ 'read' => false, 'forumid' => $data['forumid'], 'orderby' => 'modified', 'order' => 'ASC', 'row_count' => 1, 'where' => "`modified` > '" . date( 'Y-m-d H:i:s', $end_date ) . "'" ];
20
+ $topics = WPF()->topic->get_topics( $args, $items_count );
21
+ if( empty( $topics ) ) {
22
+ if( wpfval( $data, 'forum', 'last_postid' ) ) {
23
+ $last_postid = $data['forum']['last_postid'];
24
+ } elseif( wpfval( $data, 'topic', 'last_post' ) ) {
25
+ $last_postid = $data['topic']['last_post'];
26
+ }
27
+ if( ! empty( $last_postid ) ) $this->read_item( $data['forumid'], $last_postid, wpforo_prefix( 'read_forums' ) );
28
+ } else {
29
+ foreach( $topics as $topic ) {
30
+ if( wpfval( $topic, 'last_post' ) ) {
31
+ if( $items_count == 1 && $topic['topicid'] == $data['topicid'] ) {
32
+ if( wpfval( $data, 'forum', 'last_postid' ) ) {
33
+ $last_postid = $data['forum']['last_postid'];
34
+ } elseif( wpfval( $data, 'topic', 'last_post' ) ) {
35
+ $last_postid = $data['topic']['last_post'];
36
+ } elseif( wpfval( $topic, 'last_post' ) ) {
37
+ $last_postid = $topic['last_post'];
38
+ }
39
+ } else {
40
+ $last_postid = ( $topic['last_post'] - 1 );
41
+ }
42
+ if( isset( $last_postid ) ) {
43
+ $this->read_item( $data['forumid'], $last_postid, wpforo_prefix( 'read_forums' ) );
44
+ break;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ public function read_item( $itemid, $item_last_postid, $key ) {
55
+
56
+ $log = false;
57
+ $logdb = false;
58
+ $read_ids = [];
59
+ $read_db_ids = [];
60
+ $login = is_user_logged_in();
61
+
62
+ if( $itemid && $key ) {
63
+ if( wpforo_setting( 'legal', 'cookies' ) ) $read_ids = wpforo_getcookie( $key, false );
64
+ if( wpfval( WPF()->current_usermeta, $key ) ) $read_db_ids = wpforo_current_usermeta( $key );
65
+ if( ! $read_ids || ! is_array( $read_ids ) ) $read_ids = [];
66
+ if( ! $read_db_ids || ! is_array( $read_ids ) ) $read_db_ids = [];
67
+
68
+ if( empty( $read_ids ) || ( $login && empty( $read_db_ids ) ) ) {
69
+ if( empty( $read_ids ) ) $log = true;
70
+ if( empty( $read_db_ids ) && $login ) $logdb = true;
71
+ $read_ids = [ $itemid => $item_last_postid ];
72
+ } elseif( ! wpfval( $read_ids, $itemid ) || ! wpfval( $read_db_ids, $itemid ) || (int) $read_ids[ $itemid ] < (int) $item_last_postid || (int) $read_db_ids[ $itemid ] < (int) $item_last_postid ) {
73
+ $log = true;
74
+ if( $login ) $logdb = true;
75
+ if( is_array( $read_ids ) && is_array( $read_db_ids ) ) {
76
+ $keep_guest_read = apply_filters( 'wpforo_keep_guest_read', false );
77
+ $merge_guest_read = apply_filters( 'wpforo_merge_guest_read', true );
78
+ if( $merge_guest_read ) {
79
+ $read_ids = ( $keep_guest_read ) ? $read_ids + $read_db_ids : $read_db_ids + $read_ids;
80
+ } else {
81
+ $read_ids = ( $login ) ? $read_db_ids : $read_ids;
82
+ }
83
+ }
84
+ $read_ids[ $itemid ] = $item_last_postid;
85
+ if( $key === wpforo_prefix( 'read_forums' ) ) {
86
+ $read_ids = $this->read_parent_items( $read_ids, $itemid, $item_last_postid );
87
+ }
88
+ }
89
+ $this->log_item( $read_ids, $key, $logdb, $log );
90
+ }
91
+ }
92
+
93
+ public function read_parent_items( $read_ids = [], $itemid = 0, $item_last_postid = 0 ) {
94
+ if( wpfval( $read_ids, $itemid ) && $item_last_postid ) {
95
+ WPF()->forum->get_parents( $itemid, $parents );
96
+ foreach( $parents as $parent ) {
97
+ $read_ids[ $parent ] = $item_last_postid;
98
+ }
99
+ }
100
+
101
+ return $read_ids;
102
+ }
103
+
104
+ public function log_item( $read_ids, $key, $logdb = true, $log = true ) {
105
+ if( $log && wpforo_setting( 'legal', 'cookies' ) ) {
106
+ wpforo_setcookie( $key, $read_ids, false );
107
+ }
108
+ if( $logdb && is_user_logged_in() ) {
109
+ $max = apply_filters( 'wpforo_max_logged_topics', 100 );
110
+ $num = count( $read_ids );
111
+ if( $num > $max ) {
112
+ $delta = $num - $max;
113
+ if( $delta > 0 ) $read_ids = array_slice( $read_ids, $delta, null, true );
114
+ }
115
+ update_user_meta( WPF()->current_userid, $key, $read_ids );
116
+ }
117
+ }
118
+
119
+ public function unread( $id, $in = 'forum', $postid = 0 ) {
120
+
121
+ $new = false;
122
+ if( ! wpforo_setting( 'logging', 'view_logging' ) ) return false;
123
+
124
+ if( $id ) {
125
+ if( $in === 'forum' ) {
126
+ $last_postid = wpforo_forum( $id, 'last_postid' );
127
+ if( ! $last_postid ) {
128
+ return false;
129
+ }
130
+ //Pass all posts created before "Mark all read" action
131
+ $last_read_postid = $this->get_all_read( 'post' );
132
+ if( $last_read_postid ) {
133
+ if( (int) $last_postid > $last_read_postid ) $new = true;
134
+ }
135
+ //Check the last read post of current forum
136
+ if( ! $last_read_postid || $new ) {
137
+ $read_forums = $this->get_read_forums();
138
+ if( wpfkey( $read_forums, $id ) ) {
139
+ $last_read_postid = $read_forums[ $id ];
140
+ if( (int) $last_postid > (int) $last_read_postid ) {
141
+ $new = true;
142
+ } else {
143
+ $new = false;
144
+ }
145
+ } else {
146
+ $new = true;
147
+ }
148
+ }
149
+ $new = apply_filters( 'wpforo_new_in_forum', $new, $id );
150
+ } elseif( $in == 'topic' || $in == 'post' ) {
151
+ $last_postid = wpforo_topic( $id, 'last_post' );
152
+ if( ! $last_postid ) {
153
+ return false;
154
+ }
155
+ //Pass all posts created before "Mark all read" action
156
+ $last_read_postid = $this->get_all_read( 'post' );
157
+ if( $last_read_postid ) {
158
+ if( (int) $last_postid > $last_read_postid ) $new = true;
159
+ }
160
+ //Check the last read post of current forum
161
+ if( ! $last_read_postid || $new ) {
162
+ $read_topics = $this->get_read_topics();
163
+ if( wpfkey( $read_topics, $id ) ) {
164
+ $last_read_postid = $read_topics[ $id ];
165
+ if( (int) $last_postid > (int) $last_read_postid ) {
166
+ $new = true;
167
+ } else {
168
+ $new = false;
169
+ }
170
+ } else {
171
+ $new = true;
172
+ }
173
+ }
174
+ //Check an individual post if it's unread in an unread topic
175
+ if( $in === 'post' && $postid && (int) $postid > (int) $last_read_postid ) $new = true;
176
+ $new = apply_filters( 'wpforo_new_in_topic', $new, $id );
177
+ }
178
+ }
179
+
180
+ return $new;
181
+ }
182
+
183
+ public function get_read( $return = 'topicid' ) {
184
+ $topic_ids = [];
185
+ $read_topics = $this->get_read_topics();
186
+ if( ! empty( $read_topics ) && is_array( $read_topics ) ) {
187
+ $last_read_postid = $this->get_all_read( 'post' );
188
+ foreach( $read_topics as $topicid => $postid ) {
189
+ if( $last_read_postid && (int) $postid <= $last_read_postid ) {
190
+ unset( $read_topics[ $topicid ] );
191
+ } else {
192
+ $current_last_postid = wpforo_topic( $topicid, 'last_post' );
193
+ if( $current_last_postid ) {
194
+ if( (int) $current_last_postid <= (int) $postid ) {
195
+ $topic_ids[] = $topicid;
196
+ }
197
+ } else {
198
+ $topic_ids[] = $topicid;
199
+ }
200
+ }
201
+ }
202
+ }
203
+ if( $return == 'topicid' ) {
204
+ return $topic_ids;
205
+ } else {
206
+ return $read_topics;
207
+ }
208
+ }
209
+
210
+ public function get_read_forums() {
211
+ if( is_user_logged_in() && wpfval( WPF()->current_usermeta, wpforo_prefix( 'read_forums' ) ) ) {
212
+ $read_forums = wpforo_current_usermeta( wpforo_prefix( 'read_forums' ) );
213
+ } else {
214
+ $read_forums = wpforo_getcookie( wpforo_prefix( 'read_forums' ), false );
215
+ }
216
+
217
+ return $read_forums;
218
+ }
219
+
220
+ public function get_read_topics() {
221
+ if( is_user_logged_in() && wpfval( WPF()->current_usermeta, wpforo_prefix( 'read_topics' ) ) ) {
222
+ $read_topics = wpforo_current_usermeta( wpforo_prefix( 'read_topics' ) );
223
+ } else {
224
+ $read_topics = wpforo_getcookie( wpforo_prefix( 'read_topics' ), false );
225
+ }
226
+
227
+ return $read_topics;
228
+ }
229
+
230
+ public function last( $item ) {
231
+ $id = 0;
232
+
233
+ if( $item === 'forum' ) {
234
+ $id = WPF()->db->get_var( "SELECT MAX(`forumid`) FROM " . WPF()->tables->forums );
235
+ } elseif( $item === 'topic' ) {
236
+ $id = WPF()->db->get_var( "SELECT MAX(`topicid`) FROM " . WPF()->tables->topics );
237
+ } elseif( $item === 'post' ) {
238
+ $id = WPF()->db->get_var( "SELECT MAX(`postid`) FROM " . WPF()->tables->posts );
239
+ }
240
+
241
+ return intval( $id );
242
+ }
243
+
244
+ public function mark_all_read() {
245
+ $last_forumid = $this->last( 'forum' );
246
+ $last_topicid = $this->last( 'topic' );
247
+ $last_postid = $this->last( 'post' );
248
+ $last = [ 'forum' => $last_forumid, 'topic' => $last_topicid, 'post' => $last_postid ];
249
+ if( is_user_logged_in() ) {
250
+ update_user_meta( WPF()->current_userid, wpforo_prefix( 'all_read' ), $last );
251
+ update_user_meta( WPF()->current_userid, wpforo_prefix( 'read_forums' ), [] );
252
+ update_user_meta( WPF()->current_userid, wpforo_prefix( 'read_topics' ), [] );
253
+ }
254
+ wpforo_setcookie( wpforo_prefix( 'all_read' ), $last );
255
+ wpforo_setcookie( wpforo_prefix( 'read_forums' ) );
256
+ wpforo_setcookie( wpforo_prefix( 'read_topics' ) );
257
+ }
258
+
259
+ public function get_all_read( $item ) {
260
+ if( is_user_logged_in() ) {
261
+ $last = wpforo_current_usermeta( wpforo_prefix( 'all_read' ) );
262
+ } else {
263
+ $last = wpforo_getcookie( wpforo_prefix( 'all_read' ) );
264
+ }
265
+ if( $item && wpfval( $last, $item ) ) {
266
+ return intval( $last[ $item ] );
267
+ }
268
+
269
+ return 0;
270
+ }
271
+
272
+ public function visitors( $item ) {
273
+ $data = [];
274
+ $visitors = [];
275
+ if( wpfval( $item, 'topicid' ) ) {
276
+ $keep_vistors_data = apply_filters( 'wpforo_keep_visitors_data', 4000 );
277
+ $time = (int) current_time( 'timestamp', 1 ) - (int) $keep_vistors_data;
278
+ $visitors = WPF()->db->get_results( "SELECT * FROM `" . WPF()->tables->visits . "` WHERE `topicid` = " . intval( $item['topicid'] ) . " AND `time` > " . $time . " ORDER BY `id` DESC", ARRAY_A );
279
+ } elseif( wpfval( $item, 'forumid' ) ) {
280
+ $keep_vistors_data = apply_filters( 'wpforo_keep_visitors_data', 4000 );
281
+ $time = (int) current_time( 'timestamp', 1 ) - (int) $keep_vistors_data;
282
+ $visitors = WPF()->db->get_results( "SELECT * FROM `" . WPF()->tables->visits . "` WHERE `forumid` = " . intval( $item['forumid'] ) . " AND `time` > " . $time . " ORDER BY `id` DESC", ARRAY_A );
283
+ }
284
+ if( ! empty( $visitors ) ) {
285
+ $online_period = (int) current_time( 'timestamp', 1 ) - (int) wpforo_setting( 'profiles', 'online_status_timeout' );
286
+ foreach( $visitors as $visitor ) {
287
+ if( wpfval( $visitor, 'userid' ) ) {
288
+ if( (int) $visitor['time'] < $online_period ) {
289
+ $data['users']['viewed'][] = $visitor;
290
+ } else {
291
+ $gone = WPF()->db->get_var( "SELECT `id` FROM `" . WPF()->tables->visits . "` WHERE `userid` = " . intval( $visitor['userid'] ) . " AND `time` > " . intval( $visitor['time'] ) . " LIMIT 1" );
292
+ if( $gone ) {
293
+ $data['users']['viewed'][] = $visitor;
294
+ } else {
295
+ $data['users']['viewing'][] = $visitor;
296
+ }
297
+ }
298
+ } elseif( (int) $visitor['time'] > $online_period ) {
299
+ $data['guests'][] = $visitor;
300
+ }
301
+ }
302
+ }
303
+
304
+ return $data;
305
+ }
306
+
307
+ public function visit() {
308
+ if( wpforo_setting( 'logging', 'track_logging' ) ) {
309
+ if( ( wpforo_current_user_is( 'admin' ) || WPF()->current_user_groupid == 1 ) && ! wpforo_setting( 'logging', 'display_admin_viewers' ) ) {
310
+ return false;
311
+ }
312
+ $data = WPF()->current_object;
313
+ $visitor = WPF()->current_user;
314
+ $template = ( wpfval( $data, 'template' ) ) ? $data['template'] : '';
315
+ $templates = [ 'forum', 'topic', 'post' ];
316
+ $templates = apply_filters( 'wpforo_track_visitors_in_pages', $templates );
317
+ if( $template ) {
318
+ if( $template == 'post' && in_array( $template, $templates ) ) {
319
+ //topic page (post list)
320
+ if( wpfval( $data, 'topic', 'topicid' ) && wpfval( $data, 'topic', 'forumid' ) ) {
321
+ $forumid = intval( $data['topic']['forumid'] );
322
+ $topicid = intval( $data['topic']['topicid'] );
323
+ $this->add_visit( $visitor, $forumid, $topicid );
324
+ }
325
+ } elseif( $template == 'topic' && in_array( $template, $templates ) ) {
326
+ //forum page (topic list)
327
+ if( wpfval( $data, 'forum', 'forumid' ) ) {
328
+ $forumid = intval( $data['forum']['forumid'] );
329
+ $this->add_visit( $visitor, $forumid );
330
+ }
331
+ } elseif( $template == 'forum' && in_array( $template, $templates ) ) {
332
+ //forum home page
333
+ $this->add_visit( $visitor );
334
+ } elseif( in_array( $template, $templates ) ) {
335
+ //other pages (profile, search, members, etc..)
336
+ $this->add_visit( $visitor );
337
+ }
338
+ }
339
+ }
340
+ return false;
341
+ }
342
+
343
+ public function add_visit( $visitor, $forumid = 0, $topicid = 0 ) {
344
+ $time = esc_sql( current_time( 'timestamp', 1 ) );
345
+ if( WPF()->current_userid ) {
346
+ $userid = ( wpfval( $visitor, 'userid' ) ) ? intval( $visitor['userid'] ) : 0;
347
+ $name = ( wpfval( $visitor, 'display_name' ) ) ? esc_sql( $visitor['display_name'] ) : '';
348
+ WPF()->db->query( "INSERT INTO `" . WPF()->tables->visits . "` (`userid`, `name`, `time`, `topicid`, `forumid`) VALUES( " . $userid . ", '" . $name . "', '" . $time . "', " . $topicid . ", " . $forumid . " ) ON DUPLICATE KEY UPDATE `time` = '" . $time . "'" );
349
+ } elseif( ! wpforo_is_bot() ) {
350
+ $ip = ( wpfval( $_SERVER, 'REMOTE_ADDR' ) ) ? substr( md5( $_SERVER['REMOTE_ADDR'] ), 0, 32 ) : '-noip-';
351
+ if( $ip ) {
352
+ $guest = WPF()->db->get_var( "SELECT `id` FROM `" . WPF()->tables->visits . "` WHERE `ip` = '" . esc_sql( $ip ) . "' LIMIT 1" );
353
+ if( $guest ) {
354
+ WPF()->db->query( "UPDATE `" . WPF()->tables->visits . "` SET `topicid` = " . intval( $topicid ) . ", `forumid` = " . intval( $forumid ) . ", `time` = '" . $time . "' WHERE `id` = " . intval( $guest ) );
355
+ } else {
356
+ WPF()->db->query( "INSERT INTO `" . WPF()->tables->visits . "` (`ip`, `time`, `topicid`, `forumid`) VALUES( '" . esc_sql( $ip ) . "', '" . $time . "', " . $topicid . ", " . $forumid . " )" );
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ /**
363
+ * do not use, this logic is not tested yet.
364
+ *
365
+ * @param int $forumid
366
+ *
367
+ * @return int
368
+ */
369
+ public function get_forum_unreads_count( $forumid ) {
370
+ $forumid = intval( $forumid );
371
+ $read_forums = $this->get_read_forums();
372
+ $last_read_postid = (int) wpfval( $read_forums, $forumid );
373
+ $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->posts . "`
374
+ WHERE `status` = 0
375
+ AND `private` = 0
376
+ AND `forumid` = $forumid
377
+ AND `postid` > $last_read_postid";
378
+ $count = (int) WPF()->db->get_var( $sql );
379
+
380
+ return (int) apply_filters( 'wpforo_logs_get_forum_unreads_count', $count, $forumid, $last_read_postid );
381
+ }
382
+
383
+ /**
384
+ * get unread posts count for topic by giving topicid
385
+ *
386
+ * @param int $topicid
387
+ *
388
+ * @return int
389
+ */
390
+ public function get_topic_unreads_count( $topicid ) {
391
+ $topicid = intval( $topicid );
392
+ $read_topics = $this->get_read_topics();
393
+ $last_read_postid = (int) wpfval( $read_topics, $topicid );
394
+ $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->posts . "`
395
+ WHERE `status` = 0
396
+ AND `topicid` = $topicid
397
+ AND `postid` > $last_read_postid";
398
+ $count = (int) WPF()->db->get_var( $sql );
399
+
400
+ return (int) apply_filters( 'wpforo_logs_get_topic_unreads_count', $count, $topicid, $last_read_postid );
401
+ }
402
+
403
+ }
classes/Members.php ADDED
@@ -0,0 +1,3567 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace wpforo\classes;
4
+
5
+ use stdClass;
6
+ use WP_Error;
7
+ use wpforo\admin\listtables\Members as MembersListTable;
8
+ use wpforo\modules\reactions\Reactions;
9
+
10
+ // Exit if accessed directly
11
+ if( ! defined( 'ABSPATH' ) ) exit;
12
+
13
+
14
+ class Members {
15
+ private $default;
16
+ private $fields;
17
+ private $countries;
18
+ private $timezones;
19
+ public $login_min_length;
20
+ public $login_max_length;
21
+ public $pass_min_length;
22
+ public $pass_max_length;
23
+ public $list_table;
24
+ private $touched_userids = [];
25
+
26
+ function __construct() {
27
+ $this->init_defaults();
28
+ $this->init_hooks();
29
+ }
30
+
31
+ private function init_defaults() {
32
+ $this->login_min_length = 3;
33
+ $this->login_max_length = 30;
34
+ $this->pass_min_length = 6;
35
+ $this->pass_max_length = 20;
36
+ $this->default = new stdClass();
37
+ $this->default->member = [
38
+ 'user_login' => '',
39
+ 'user_nicename' => '',
40
+ 'user_email' => '',
41
+ 'user_url' => '',
42
+ 'user_registered' => '0000-00-00 00:00:00',
43
+ 'user_activation_key' => '',
44
+ 'display_name' => '',
45
+
46
+ 'first_name' => '',
47
+ 'last_name' => '',
48
+
49
+ 'userid' => 0,
50
+ 'title' => '',
51
+ 'groupid' => 0,
52
+ 'secondary_groupids' => [],
53
+ 'groupids' => [],
54
+ 'avatar' => '',
55
+ 'cover' => '',
56
+
57
+ 'posts' => 0,
58
+ 'topics' => 0,
59
+ 'questions' => 0,
60
+ 'answers' => 0,
61
+ 'comments' => 0,
62
+ 'reactions_in' => [],
63
+ 'reactions_out' => [],
64
+ 'points' => 0,
65
+ 'custom_points' => 0,
66
+
67
+ 'online_time' => 0,
68
+ 'timezone' => 'UTC+0',
69
+ 'location' => '',
70
+ 'signature' => '',
71
+ 'occupation' => '',
72
+ 'about' => '',
73
+ 'status' => 'inactive',
74
+ 'is_email_confirmed' => 0,
75
+ 'is_mention_muted' => 0,
76
+
77
+ 'fields' => [],
78
+
79
+ 'group_name' => '',
80
+ 'group_color' => '',
81
+
82
+ 'profile_url' => '',
83
+ 'rating' => [
84
+ 'level' => 0,
85
+ 'percent' => 0,
86
+ 'color' => $this->rating( 0, 'color' ),
87
+ 'title' => $this->rating( 0, 'title' ),
88
+ 'badge' => $this->rating( 0, 'icon' ),
89
+ ],
90
+ ];
91
+ $this->default->action = [
92
+ 'label' => 'Action Button',
93
+ 'ico' => '<i class="fas fa-user"></i>',
94
+ 'callback_for_can' => '',
95
+ 'callback_for_get_url' => '',
96
+ 'data' => [],
97
+ 'confirm_msg' => '',
98
+ ];
99
+ }
100
+
101
+ private function init_hooks() {
102
+ if( is_admin() ) add_action( 'wpforo_after_init', [ $this, 'init_list_table' ] );
103
+ add_action( 'delete_user_form', [ $this, 'show_delete_form' ], 10, 2 );
104
+ add_action( 'register_new_user', [ $this, 'after_register_new_user' ] );
105
+ add_action( 'after_password_reset', [ $this, 'after_password_reset' ] );
106
+ add_action( 'wp_login', [ $this, 'wp_login' ], 10, 2 );
107
+ add_action( 'set_current_user', [ $this, 'init_current_user' ] );
108
+ add_action( 'clean_user_cache', function( $userid ){ $this->reset( $userid ); } );
109
+ add_action( 'init', function() { if( ! WPF()->current_userid ) $this->init_current_user(); } );
110
+ add_action( 'set_logged_in_cookie', function( $logged_in_cookie ) {
111
+ $cookie = explode( '|', $logged_in_cookie );
112
+ WPF()->session_token = (string) wpfval( $cookie, 2 );
113
+ } );
114
+
115
+ add_action( 'wpforo_after_add_topic', [$this, 'after_add_topic'] );
116
+ add_action( 'wpforo_after_delete_topic', [$this, 'after_delete_topic'] );
117
+ add_action( 'wpforo_topic_status_update', [$this, 'after_topic_status_update'] );
118
+ add_action( 'wpforo_before_merge_topic', [$this, 'before_merge_topic'], 10, 3 );
119
+ add_action( 'wpforo_after_merge_topic', [$this, 'after_merge_topic'] );
120
+ add_action( 'wpforo_after_move_topic', [$this, 'after_move_topic'] );
121
+
122
+ add_action( 'wpforo_after_add_post', [$this, 'after_add_post'] );
123
+ add_action( 'wpforo_after_delete_post', [$this, 'after_delete_post'] );
124
+ add_action( 'wpforo_post_status_update', [$this, 'after_post_status_update'] );
125
+
126
+ add_action( 'wpforo_after_add_reaction', [$this, 'after_add_reaction'] );
127
+ add_action( 'wpforo_after_edit_reaction', [$this, 'after_edit_reaction'], 10, 2 );
128
+ add_action( 'wpforo_before_delete_reaction', [$this, 'before_delete_reaction'], 10, 2 );
129
+ add_action( 'wpforo_after_delete_reaction', [$this, 'after_delete_reaction'] );
130
+
131
+ add_action( 'wpforo_after_delete_user', [$this, 'after_delete_user'], 11, 2 );
132
+ }
133
+
134
+ public function init_list_table() {
135
+ if( wpfval( $_GET, 'page' ) === wpforo_prefix_slug( 'members' ) ) {
136
+ $this->list_table = new MembersListTable();
137
+ $this->list_table->prepare_items();
138
+ }
139
+ }
140
+
141
+ /**
142
+ * @param array $member
143
+ *
144
+ * @return array
145
+ */
146
+ public function decode( $member ) {
147
+ $reaction_types = array_map( '__return_zero', array_flip( Reactions::get_type_list() ) );
148
+ $member = array_merge( $this->default->member, (array) $member );
149
+ $member['userid'] = wpforo_bigintval( $member['userid'] );
150
+ $member['first_name'] = trim( strip_tags( $member['first_name'] ) );
151
+ $member['last_name'] = trim( strip_tags( $member['last_name'] ) );
152
+ $member['groupid'] = intval( $member['groupid'] );
153
+ $member['secondary_groupids'] = array_map( 'intval', (array) ( is_scalar( $member['secondary_groupids'] ) ? explode( ',', $member['secondary_groupids'] ) : $member['secondary_groupids'] ) );
154
+ $member['secondary_groupids'] = array_diff( $member['secondary_groupids'],