class-fl-builder.php 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301
  1. <?php
  2. /**
  3. * Main builder class.
  4. *
  5. * @since 1.0
  6. */
  7. final class FLBuilder {
  8. /**
  9. * The ID of a post that is currently being rendered.
  10. *
  11. * @since 1.6.4.2
  12. * @var int $post_rendering
  13. */
  14. static public $post_rendering = null;
  15. /**
  16. * Stores the default directory name to look for in a theme for BB templates.
  17. *
  18. * @since 1.5.9-cf
  19. * @var string $template_dir
  20. */
  21. static private $template_dir = 'fl-builder/includes';
  22. /**
  23. * An array of asset paths that have already been rendered. This is
  24. * used to ensure that the same asset isn't rendered twice on the same
  25. * page. That typically can happen when you do things like insert the
  26. * same layout twice using the fl_builder_insert_layout shortcode.
  27. *
  28. * @since 2.0
  29. * @var bool $rendered_assets
  30. */
  31. static private $rendered_assets = array();
  32. /**
  33. * An array of which global assets have already been enqueued. This is
  34. * used to ensure that only one copy of either the global CSS or JS is
  35. * ever loaded on the page at one time.
  36. *
  37. * For example, if a layout CSS file with the global CSS included in it
  38. * has already been enqueued, subsequent layout CSS files will not include
  39. * the global CSS.
  40. *
  41. * @since 1.8.2
  42. * @var bool $enqueued_global_assets
  43. */
  44. static private $enqueued_global_assets = array();
  45. /**
  46. * Used to store JS that is to be rendered inline on the wp_footer
  47. * action when the fl_builder_render_assets_inline filter is true.
  48. *
  49. * @since 2.1
  50. * @var string $inline_js
  51. */
  52. static private $inline_js = '';
  53. /**
  54. * Font awesome urls.
  55. * @since 2.1
  56. */
  57. static public $fa4_url = 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css';
  58. static public $fa5_pro_url = 'https://pro.fontawesome.com/releases/v5.2.0/css/all.css';
  59. /**
  60. * Initializes hooks.
  61. *
  62. * @since 1.8
  63. * @return void
  64. */
  65. static public function init() {
  66. /* Actions */
  67. add_action( 'plugins_loaded', __CLASS__ . '::load_plugin_textdomain' );
  68. add_action( 'send_headers', __CLASS__ . '::no_cache_headers' );
  69. add_action( 'wp', __CLASS__ . '::init_ui', 11 );
  70. add_action( 'wp', __CLASS__ . '::rich_edit' );
  71. add_action( 'wp_enqueue_scripts', __CLASS__ . '::register_layout_styles_scripts' );
  72. add_action( 'wp_enqueue_scripts', __CLASS__ . '::enqueue_ui_styles_scripts', 11 );
  73. add_action( 'wp_enqueue_scripts', __CLASS__ . '::enqueue_all_layouts_styles_scripts' );
  74. add_action( 'wp_head', __CLASS__ . '::render_custom_css_for_editing', 999 );
  75. add_action( 'admin_bar_menu', __CLASS__ . '::admin_bar_menu', 999 );
  76. add_action( 'wp_footer', __CLASS__ . '::include_jquery' );
  77. add_action( 'wp_footer', __CLASS__ . '::render_ui' );
  78. /* Filters */
  79. add_filter( 'fl_builder_render_css', __CLASS__ . '::rewrite_css_cache_urls', 9999 );
  80. add_filter( 'body_class', __CLASS__ . '::body_class' );
  81. add_filter( 'wp_default_editor', __CLASS__ . '::default_editor' );
  82. add_filter( 'mce_css', __CLASS__ . '::add_editor_css' );
  83. add_filter( 'mce_buttons', __CLASS__ . '::editor_buttons' );
  84. add_filter( 'mce_buttons_2', __CLASS__ . '::editor_buttons_2' );
  85. add_filter( 'mce_external_plugins', __CLASS__ . '::editor_external_plugins', 9999 );
  86. add_filter( 'tiny_mce_before_init', __CLASS__ . '::editor_font_sizes' );
  87. add_filter( 'the_content', __CLASS__ . '::render_content' );
  88. add_filter( 'wp_handle_upload_prefilter', __CLASS__ . '::wp_handle_upload_prefilter_filter' );
  89. }
  90. /**
  91. * Localization
  92. *
  93. * Load the translation file for current language. Checks the default WordPress
  94. * languages folder first and then the languages folder inside the plugin.
  95. *
  96. * @since 1.4.4
  97. * @return string|bool The translation file path or false if none is found.
  98. */
  99. static public function load_plugin_textdomain() {
  100. // Traditional WordPress plugin locale filter
  101. // Uses get_user_locale() which was added in 4.7 so we need to check its available.
  102. if ( function_exists( 'get_user_locale' ) ) {
  103. $locale = apply_filters( 'plugin_locale', get_user_locale(), 'fl-builder' );
  104. } else {
  105. $locale = apply_filters( 'plugin_locale', get_locale(), 'fl-builder' );
  106. }
  107. //Setup paths to current locale file
  108. $mofile_global = trailingslashit( WP_LANG_DIR ) . 'plugins/bb-plugin/' . $locale . '.mo';
  109. $mofile_local = trailingslashit( FL_BUILDER_DIR ) . 'languages/' . $locale . '.mo';
  110. if ( file_exists( $mofile_global ) ) {
  111. //Look in global /wp-content/languages/plugins/bb-plugin/ folder
  112. return load_textdomain( 'fl-builder', $mofile_global );
  113. } elseif ( file_exists( $mofile_local ) ) {
  114. //Look in local /wp-content/plugins/bb-plugin/languages/ folder
  115. return load_textdomain( 'fl-builder', $mofile_local );
  116. }
  117. //Nothing found
  118. return false;
  119. }
  120. static public function rich_edit() {
  121. if ( FLBuilderModel::is_builder_active() ) {
  122. add_filter( 'get_user_option_rich_editing', '__return_true' );
  123. }
  124. }
  125. /**
  126. * Alias method for registering a template data file with the builder.
  127. *
  128. * @since 1.8
  129. * @param sting $path The directory path to the template data file.
  130. * @return void
  131. */
  132. static public function register_templates( $path, $args = array() ) {
  133. FLBuilderModel::register_templates( $path, $args );
  134. }
  135. /**
  136. * Alias method for registering a module with the builder.
  137. *
  138. * @since 1.0
  139. * @param string $class The module's PHP class name.
  140. * @param array $form The module's settings form data.
  141. * @return void
  142. */
  143. static public function register_module( $class, $form ) {
  144. FLBuilderModel::register_module( $class, $form );
  145. }
  146. /**
  147. * Alias method for registering module aliases with the builder.
  148. *
  149. * @since 1.10
  150. * @param string $alias The alias key.
  151. * @param array $config The alias config.
  152. * @return void
  153. */
  154. static public function register_module_alias( $alias, $config ) {
  155. FLBuilderModel::register_module_alias( $alias, $config );
  156. }
  157. /**
  158. * Alias method for registering a settings form with the builder.
  159. *
  160. * @since 1.0
  161. * @param string $id The form's ID.
  162. * @param array $form The form data.
  163. * @return void
  164. */
  165. static public function register_settings_form( $id, $form ) {
  166. FLBuilderModel::register_settings_form( $id, $form );
  167. }
  168. /**
  169. * Send no cache headers when the builder interface is active.
  170. *
  171. * @since 1.0
  172. * @return void
  173. */
  174. static public function no_cache_headers() {
  175. if ( isset( $_GET['fl_builder'] ) ) {
  176. header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
  177. header( 'Cache-Control: no-store, no-cache, must-revalidate' );
  178. header( 'Cache-Control: post-check=0, pre-check=0', false );
  179. header( 'Pragma: no-cache' );
  180. header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' );
  181. }
  182. }
  183. /**
  184. * Returns the markup for creating new WP editors in the builder.
  185. *
  186. * @since 2.0
  187. * @return string
  188. */
  189. static public function get_wp_editor() {
  190. ob_start();
  191. wp_editor( '{FL_EDITOR_CONTENT}', 'flbuildereditor', apply_filters( 'fl_get_wp_editor_args', array(
  192. 'media_buttons' => true,
  193. 'wpautop' => true,
  194. 'textarea_rows' => 16,
  195. ) ) );
  196. return ob_get_clean();
  197. }
  198. /**
  199. * Set the default text editor to tinymce when the builder is active.
  200. *
  201. * @since 1.0
  202. * @param string $type The current default editor type.
  203. * @return string
  204. */
  205. static public function default_editor( $type ) {
  206. return FLBuilderModel::is_builder_active() ? 'tinymce' : $type;
  207. }
  208. /**
  209. * Add custom CSS for the builder to the text editor.
  210. *
  211. * @since 1.0
  212. * @param string $mce_css
  213. * @return string
  214. */
  215. static public function add_editor_css( $mce_css ) {
  216. if ( FLBuilderModel::is_builder_active() ) {
  217. if ( ! empty( $mce_css ) ) {
  218. $mce_css .= ',';
  219. }
  220. $mce_css .= FL_BUILDER_URL . 'css/editor.css';
  221. }
  222. return $mce_css;
  223. }
  224. /**
  225. * Filter text editor buttons for the first row
  226. *
  227. * @since 1.0
  228. * @param array $buttons The current buttons array.
  229. * @return array
  230. */
  231. static public function editor_buttons( $buttons ) {
  232. if ( FLBuilderModel::is_builder_active() ) {
  233. if ( ( $key = array_search( 'wp_more', $buttons ) ) !== false ) { // @codingStandardsIgnoreLine
  234. unset( $buttons[ $key ] );
  235. }
  236. }
  237. return $buttons;
  238. }
  239. /**
  240. * Add additional buttons to the text editor.
  241. *
  242. * @since 1.0
  243. * @param array $buttons The current buttons array.
  244. * @return array
  245. */
  246. static public function editor_buttons_2( $buttons ) {
  247. global $wp_version;
  248. if ( FLBuilderModel::is_builder_active() ) {
  249. array_shift( $buttons );
  250. array_unshift( $buttons, 'fontsizeselect' );
  251. if ( version_compare( $wp_version, '4.6.9', '<=' ) ) {
  252. array_unshift( $buttons, 'formatselect' );
  253. }
  254. if ( ( $key = array_search( 'wp_help', $buttons ) ) !== false ) { // @codingStandardsIgnoreLine
  255. unset( $buttons[ $key ] );
  256. }
  257. }
  258. return $buttons;
  259. }
  260. /**
  261. * Custom font size options for the editor font size select.
  262. *
  263. * @since 1.6.3
  264. * @param array $init The TinyMCE init array.
  265. * @return array
  266. */
  267. static public function editor_font_sizes( $init ) {
  268. if ( FLBuilderModel::is_builder_active() ) {
  269. $init['fontsize_formats'] = implode( ' ', array(
  270. '10px',
  271. '12px',
  272. '14px',
  273. '16px',
  274. '18px',
  275. '20px',
  276. '22px',
  277. '24px',
  278. '26px',
  279. '28px',
  280. '30px',
  281. '32px',
  282. '34px',
  283. '36px',
  284. '38px',
  285. '40px',
  286. '42px',
  287. '44px',
  288. '46px',
  289. '48px',
  290. ));
  291. }
  292. return $init;
  293. }
  294. /**
  295. * Only allows certain text editor plugins to avoid conflicts
  296. * with third party plugins.
  297. *
  298. * @since 1.0
  299. * @param array $plugins The current editor plugins.
  300. * @return array
  301. */
  302. static public function editor_external_plugins( $plugins ) {
  303. if ( FLBuilderModel::is_builder_active() ) {
  304. $allowed = array(
  305. 'anchor',
  306. 'code',
  307. 'insertdatetime',
  308. 'nonbreaking',
  309. 'print',
  310. 'searchreplace',
  311. 'table',
  312. 'visualblocks',
  313. 'visualchars',
  314. 'emoticons',
  315. 'advlist',
  316. 'wptadv',
  317. );
  318. foreach ( $plugins as $key => $val ) {
  319. if ( ! in_array( $key, $allowed ) ) {
  320. unset( $plugins[ $key ] );
  321. }
  322. }
  323. }
  324. return $plugins;
  325. }
  326. /**
  327. * Register the styles and scripts for builder layouts.
  328. *
  329. * @since 1.7.4
  330. * @return void
  331. */
  332. static public function register_layout_styles_scripts() {
  333. $ver = FL_BUILDER_VERSION;
  334. $css_url = plugins_url( '/css/', FL_BUILDER_FILE );
  335. $js_url = plugins_url( '/js/', FL_BUILDER_FILE );
  336. $min = ( self::is_debug() ) ? '' : '.min';
  337. // Register additional CSS
  338. wp_register_style( 'fl-slideshow', $css_url . 'fl-slideshow.css', array( 'yui3' ), $ver );
  339. wp_register_style( 'jquery-bxslider', $css_url . 'jquery.bxslider.css', array(), $ver );
  340. wp_register_style( 'jquery-magnificpopup', $css_url . 'jquery.magnificpopup.css', array(), $ver );
  341. wp_register_style( 'yui3', $css_url . 'yui3.css', array(), $ver );
  342. // Register icon CDN CSS
  343. wp_register_style( 'font-awesome', self::$fa4_url, array(), $ver );
  344. wp_register_style( 'font-awesome-5', self::get_fa5_url(), array(), $ver );
  345. wp_register_style( 'foundation-icons', 'https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.css', array(), $ver );
  346. // Register additional JS
  347. wp_register_script( 'fl-slideshow', $js_url . 'fl-slideshow' . $min . '.js', array( 'yui3' ), $ver, true );
  348. wp_register_script( 'fl-gallery-grid', $js_url . 'fl-gallery-grid.js', array( 'jquery' ), $ver, true );
  349. wp_register_script( 'jquery-bxslider', $js_url . 'jquery.bxslider.js', array( 'jquery-easing', 'jquery-fitvids' ), $ver, true );
  350. wp_register_script( 'jquery-easing', $js_url . 'jquery.easing.min.js', array( 'jquery' ), '1.4', true );
  351. wp_register_script( 'jquery-fitvids', $js_url . 'jquery.fitvids.min.js', array( 'jquery' ), '1.2', true );
  352. wp_register_script( 'jquery-imagesloaded', $js_url . 'jquery.imagesloaded.min.js', array( 'jquery' ), $ver, true );
  353. wp_register_script( 'jquery-infinitescroll', $js_url . 'jquery.infinitescroll.min.js', array( 'jquery' ), $ver, true );
  354. wp_register_script( 'jquery-magnificpopup', $js_url . 'jquery.magnificpopup.min.js', array( 'jquery' ), $ver, true );
  355. wp_register_script( 'jquery-mosaicflow', $js_url . 'jquery.mosaicflow.min.js', array( 'jquery' ), $ver, true );
  356. wp_register_script( 'jquery-waypoints', $js_url . 'jquery.waypoints.min.js', array( 'jquery' ), $ver, true );
  357. wp_register_script( 'jquery-wookmark', $js_url . 'jquery.wookmark.min.js', array( 'jquery' ), $ver, true );
  358. wp_register_script( 'yui3', $js_url . 'yui3.min.js', array(), $ver, true );
  359. wp_register_script( 'youtube-player', 'https://www.youtube.com/iframe_api', array(), $ver, true );
  360. wp_register_script( 'vimeo-player', 'https://player.vimeo.com/api/player.js', array(), $ver, true );
  361. }
  362. /**
  363. * Enqueue the styles and scripts for all builder layouts
  364. * in the main WordPress query.
  365. *
  366. * @since 1.7.4
  367. * @return void
  368. */
  369. static public function enqueue_all_layouts_styles_scripts() {
  370. global $wp_query;
  371. global $post;
  372. $original_post = $post;
  373. $is_archive = is_archive() || is_home() || is_search();
  374. // Enqueue assets for posts in the main query.
  375. if ( ! $is_archive && isset( $wp_query->posts ) ) {
  376. foreach ( $wp_query->posts as $post ) {
  377. self::enqueue_layout_styles_scripts();
  378. }
  379. }
  380. // Enqueue assets for posts via the fl_builder_global_posts filter.
  381. $post_ids = FLBuilderModel::get_global_posts();
  382. if ( count( $post_ids ) > 0 ) {
  383. $posts = get_posts(array(
  384. 'post__in' => $post_ids,
  385. 'post_type' => get_post_types(),
  386. 'posts_per_page' => -1,
  387. ));
  388. foreach ( $posts as $post ) {
  389. self::enqueue_layout_styles_scripts();
  390. }
  391. }
  392. // Reset the global post variable.
  393. $post = $original_post;
  394. }
  395. /**
  396. * Enqueue the styles and scripts for a single layout.
  397. *
  398. * @since 1.0
  399. * @param bool $rerender Whether to rerender the CSS and JS.
  400. * @return void
  401. */
  402. static public function enqueue_layout_styles_scripts( $rerender = false ) {
  403. if ( FLBuilderModel::is_builder_enabled() ) {
  404. $nodes = FLBuilderModel::get_categorized_nodes();
  405. // Enqueue required row CSS and JS
  406. foreach ( $nodes['rows'] as $row ) {
  407. if ( 'slideshow' == $row->settings->bg_type ) {
  408. wp_enqueue_script( 'yui3' );
  409. wp_enqueue_script( 'fl-slideshow' );
  410. wp_enqueue_script( 'jquery-imagesloaded' );
  411. wp_enqueue_style( 'fl-slideshow' );
  412. } elseif ( 'video' == $row->settings->bg_type ) {
  413. wp_enqueue_script( 'jquery-imagesloaded' );
  414. if ( 'video_service' == $row->settings->bg_video_source ) {
  415. $video_data = FLBuilderUtils::get_video_data( $row->settings->bg_video_service_url );
  416. if ( 'youtube' == $video_data['type'] ) {
  417. wp_enqueue_script( 'youtube-player' );
  418. } elseif ( 'vimeo' == $video_data['type'] ) {
  419. wp_enqueue_script( 'vimeo-player' );
  420. }
  421. }
  422. }
  423. }
  424. // Enqueue required module CSS and JS
  425. foreach ( $nodes['modules'] as $module ) {
  426. $module->enqueue_icon_styles();
  427. $module->enqueue_font_styles();
  428. $module->enqueue_scripts();
  429. foreach ( $module->css as $handle => $props ) {
  430. wp_enqueue_style( $handle, $props[0], $props[1], $props[2], $props[3] );
  431. }
  432. foreach ( $module->js as $handle => $props ) {
  433. wp_enqueue_script( $handle, $props[0], $props[1], $props[2], $props[3] );
  434. }
  435. if ( ! empty( $module->settings->animation ) ) {
  436. wp_enqueue_script( 'jquery-waypoints' );
  437. }
  438. }
  439. // Enqueue Google Fonts
  440. FLBuilderFonts::enqueue_google_fonts();
  441. // Enqueue layout CSS
  442. self::enqueue_layout_cached_asset( 'css', $rerender );
  443. // Enqueue layout JS
  444. self::enqueue_layout_cached_asset( 'js', $rerender );
  445. }
  446. }
  447. /**
  448. * Enqueue the styles and scripts for a single layout
  449. * using the provided post ID.
  450. *
  451. * @since 1.10
  452. * @param int $post_id
  453. * @return void
  454. */
  455. static public function enqueue_layout_styles_scripts_by_id( $post_id ) {
  456. FLBuilderModel::set_post_id( $post_id );
  457. FLBuilder::enqueue_layout_styles_scripts();
  458. FLBuilderModel::reset_post_id();
  459. }
  460. /**
  461. * Enqueues the cached CSS or JS asset for a layout.
  462. *
  463. * @since 1.8.2
  464. * @access private
  465. * @param string $type The type of asset. Either CSS or JS.
  466. * @param bool $rerender Whether to rerender the CSS or JS.
  467. * @return string
  468. */
  469. static private function enqueue_layout_cached_asset( $type = 'css', $rerender = false ) {
  470. $post_id = FLBuilderModel::get_post_id();
  471. $asset_info = FLBuilderModel::get_asset_info();
  472. $asset_ver = FLBuilderModel::get_asset_version();
  473. $active = FLBuilderModel::is_builder_active();
  474. $preview = FLBuilderModel::is_builder_draft_preview();
  475. $handle = 'fl-builder-layout-' . $post_id;
  476. /**
  477. * Use this filter to add dependencies to the dependency array when the main builder layout CSS file is enqueued using wp_enqueue_style.
  478. * @see fl_builder_layout_style_dependencies
  479. * @link https://kb.wpbeaverbuilder.com/article/117-plugin-filter-reference
  480. */
  481. $css_deps = apply_filters( 'fl_builder_layout_style_dependencies', array() );
  482. $css_media = apply_filters( 'fl_builder_layout_style_media', 'all' );
  483. // Enqueue with the global code included?
  484. if ( in_array( 'global-' . $type, self::$enqueued_global_assets ) ) {
  485. $path = $asset_info[ $type . '_partial' ];
  486. $url = $asset_info[ $type . '_partial_url' ];
  487. $global = false;
  488. } else {
  489. $path = $asset_info[ $type ];
  490. $url = $asset_info[ $type . '_url' ];
  491. $global = true;
  492. self::$enqueued_global_assets[] = 'global-' . $type;
  493. }
  494. // Render the asset inline instead of enqueuing the file?
  495. if ( ! $active && apply_filters( 'fl_builder_render_assets_inline', false ) ) {
  496. // Bail if we've already rendered this.
  497. if ( in_array( $path, self::$rendered_assets ) ) {
  498. return;
  499. }
  500. // Enqueue inline.
  501. if ( 'css' === $type ) {
  502. wp_register_style( $handle, false, $css_deps, $asset_ver, $css_media );
  503. wp_enqueue_style( $handle );
  504. wp_add_inline_style( $handle, self::render_css( $global, false ) );
  505. } else {
  506. self::$inline_js .= self::render_js( $global, false );
  507. if ( ! has_action( 'wp_footer', __CLASS__ . '::render_inline_js' ) ) {
  508. add_action( 'wp_footer', __CLASS__ . '::render_inline_js', PHP_INT_MAX );
  509. }
  510. }
  511. } else {
  512. // Render if the file doesn't exist.
  513. if ( ! in_array( $path, self::$rendered_assets ) && ( ! fl_builder_filesystem()->file_exists( $path ) || $rerender || $preview || self::is_debug() ) ) {
  514. call_user_func_array( array( 'FLBuilder', 'render_' . $type ), array( $global ) );
  515. self::$rendered_assets[] = $path;
  516. }
  517. // Don't enqueue if we don't have a file after trying to render.
  518. if ( ! fl_builder_filesystem()->file_exists( $path ) || 0 === fl_builder_filesystem()->filesize( $path ) ) {
  519. return;
  520. }
  521. // Enqueue.
  522. if ( 'css' == $type ) {
  523. wp_enqueue_style( $handle, $url, $css_deps, $asset_ver, $css_media );
  524. } elseif ( 'js' == $type ) {
  525. wp_enqueue_script( $handle, $url, array( 'jquery' ), $asset_ver, true );
  526. }
  527. }
  528. }
  529. /**
  530. *
  531. *
  532. * @since 2.1
  533. * @return void
  534. */
  535. static public function render_inline_js() {
  536. echo '<script>' . self::$inline_js . '</script>';
  537. }
  538. /**
  539. * Clears the enqueued global assets cache to ensure new asset
  540. * renders include global node assets.
  541. *
  542. * @since 1.10.2
  543. * @return void
  544. */
  545. static public function clear_enqueued_global_assets() {
  546. self::$enqueued_global_assets = array();
  547. }
  548. /**
  549. * Register and enqueue the styles and scripts for the builder UI.
  550. *
  551. * @since 1.7.4
  552. * @return void
  553. */
  554. static public function enqueue_ui_styles_scripts() {
  555. if ( FLBuilderModel::is_builder_active() ) {
  556. global $wp_the_query;
  557. // Remove wp admin bar top margin
  558. remove_action( 'wp_head', '_admin_bar_bump_cb' );
  559. $ver = FL_BUILDER_VERSION;
  560. $css_url = plugins_url( '/css/', FL_BUILDER_FILE );
  561. $js_url = plugins_url( '/js/', FL_BUILDER_FILE );
  562. /* Frontend builder styles */
  563. wp_enqueue_style( 'dashicons' );
  564. /**
  565. * FA4 css and FA5 css do not mix well and actually break some of the icvons in the selector.
  566. */
  567. if ( in_array( 'font-awesome', FLBuilderModel::get_enabled_icons() ) ) {
  568. wp_enqueue_style( 'font-awesome' );
  569. }
  570. wp_enqueue_style( 'font-awesome-5' );
  571. wp_enqueue_style( 'foundation-icons' );
  572. wp_enqueue_style( 'jquery-nanoscroller', $css_url . 'jquery.nanoscroller.css', array(), $ver );
  573. wp_enqueue_style( 'jquery-autosuggest', $css_url . 'jquery.autoSuggest.min.css', array(), $ver );
  574. wp_enqueue_style( 'jquery-tiptip', $css_url . 'jquery.tiptip.css', array(), $ver );
  575. wp_enqueue_style( 'bootstrap-tour', $css_url . 'bootstrap-tour-standalone.min.css', array(), $ver );
  576. // Enqueue individual builder styles if WP_DEBUG is on.
  577. if ( self::is_debug() ) {
  578. wp_enqueue_style( 'fl-color-picker', $css_url . 'fl-color-picker.css', array(), $ver );
  579. wp_enqueue_style( 'fl-lightbox', $css_url . 'fl-lightbox.css', array(), $ver );
  580. wp_enqueue_style( 'fl-icon-selector', $css_url . 'fl-icon-selector.css', array(), $ver );
  581. wp_enqueue_style( 'fl-builder', $css_url . 'fl-builder.css', array(), $ver );
  582. // skins need to come after default ui styles
  583. wp_enqueue_style( 'fl-builder-ui-skin-dark', $css_url . 'fl-builder-ui-skin-dark.css', array(), $ver );
  584. wp_enqueue_style( 'fl-builder-bundle', $css_url . 'build/builder.bundle.css', array(), $ver );
  585. } else {
  586. wp_enqueue_style( 'fl-builder-min', $css_url . 'fl-builder.min.css', array(), $ver );
  587. wp_enqueue_style( 'fl-builder-bundle', $css_url . 'build/builder.bundle.min.css', array(), $ver );
  588. }
  589. /* Custom Icons */
  590. FLBuilderIcons::enqueue_all_custom_icons_styles();
  591. /* RTL Support */
  592. if ( is_rtl() ) {
  593. wp_enqueue_style( 'fl-builder-rtl', $css_url . 'fl-builder-rtl.css', array(), $ver );
  594. }
  595. /* We have a custom version of sortable that fixes a bug. */
  596. wp_deregister_script( 'jquery-ui-sortable' );
  597. /* Frontend builder scripts */
  598. wp_enqueue_media();
  599. wp_enqueue_script( 'heartbeat' );
  600. wp_enqueue_script( 'wpdialogs' );
  601. wp_enqueue_script( 'wpdialogs-popup' );
  602. wp_enqueue_script( 'wplink' );
  603. wp_enqueue_script( 'editor' );
  604. wp_enqueue_script( 'quicktags' );
  605. wp_enqueue_script( 'json2' );
  606. wp_enqueue_script( 'jquery-ui-droppable' );
  607. wp_enqueue_script( 'jquery-ui-draggable' );
  608. wp_enqueue_script( 'jquery-ui-slider' );
  609. wp_enqueue_script( 'jquery-ui-widget' );
  610. wp_enqueue_script( 'jquery-ui-position' );
  611. do_action( 'fl_before_sortable_enqueue' );
  612. wp_enqueue_script( 'jquery-ui-sortable', $js_url . 'jquery.ui.sortable.js', array( 'jquery-ui-core', 'jquery-ui-widget', 'jquery-ui-mouse' ), $ver );
  613. wp_enqueue_script( 'jquery-nanoscroller', $js_url . 'jquery.nanoscroller.min.js', array(), $ver );
  614. wp_enqueue_script( 'jquery-autosuggest', $js_url . 'jquery.autoSuggest.min.js', array(), $ver );
  615. wp_enqueue_script( 'jquery-tiptip', $js_url . 'jquery.tiptip.min.js', array(), $ver );
  616. wp_enqueue_script( 'jquery-showhideevents', $js_url . 'jquery.showhideevents.js', array(), $ver );
  617. wp_enqueue_script( 'jquery-simulate', $js_url . 'jquery.simulate.js', array(), $ver );
  618. wp_enqueue_script( 'jquery-validate', $js_url . 'jquery.validate.min.js', array(), $ver );
  619. wp_enqueue_script( 'bootstrap-tour', $js_url . 'bootstrap-tour-standalone.min.js', array(), $ver );
  620. wp_enqueue_script( 'ace', $js_url . 'ace/ace.js', array(), $ver );
  621. wp_enqueue_script( 'ace-language-tools', $js_url . 'ace/ext-language_tools.js', array(), $ver );
  622. wp_enqueue_script( 'mousetrap', $js_url . 'mousetrap-custom.js', array(), $ver );
  623. // Enqueue individual builder scripts if WP_DEBUG is on.
  624. if ( self::is_debug() ) {
  625. wp_enqueue_script( 'fl-color-picker', $js_url . 'fl-color-picker.js', array(), $ver );
  626. wp_enqueue_script( 'fl-lightbox', $js_url . 'fl-lightbox.js', array(), $ver );
  627. wp_enqueue_script( 'fl-icon-selector', $js_url . 'fl-icon-selector.js', array(), $ver );
  628. wp_enqueue_script( 'fl-stylesheet', $js_url . 'fl-stylesheet.js', array(), $ver );
  629. wp_enqueue_script( 'fl-builder', $js_url . 'fl-builder.js', array(), $ver );
  630. wp_enqueue_script( 'fl-builder-ajax-layout', $js_url . 'fl-builder-ajax-layout.js', array(), $ver );
  631. wp_enqueue_script( 'fl-builder-preview', $js_url . 'fl-builder-preview.js', array(), $ver );
  632. wp_enqueue_script( 'fl-builder-simulate-media-query', $js_url . 'fl-builder-simulate-media-query.js', array(), $ver );
  633. wp_enqueue_script( 'fl-builder-responsive-editing', $js_url . 'fl-builder-responsive-editing.js', array(), $ver );
  634. wp_enqueue_script( 'fl-builder-responsive-preview', $js_url . 'fl-builder-responsive-preview.js', array(), $ver );
  635. wp_enqueue_script( 'fl-builder-services', $js_url . 'fl-builder-services.js', array(), $ver );
  636. wp_enqueue_script( 'fl-builder-tour', $js_url . 'fl-builder-tour.js', array(), $ver );
  637. wp_enqueue_script( 'fl-builder-ui', $js_url . 'fl-builder-ui.js', array( 'fl-builder', 'mousetrap' ), $ver );
  638. wp_enqueue_script( 'fl-builder-ui-main-menu', $js_url . 'fl-builder-ui-main-menu.js', array( 'fl-builder-ui' ), $ver );
  639. wp_enqueue_script( 'fl-builder-ui-panel-content', $js_url . 'fl-builder-ui-panel-content-library.js', array( 'fl-builder-ui' ), $ver );
  640. wp_enqueue_script( 'fl-builder-ui-settings-forms', $js_url . 'fl-builder-ui-settings-forms.js', array(), $ver );
  641. wp_enqueue_script( 'fl-builder-ui-pinned', $js_url . 'fl-builder-ui-pinned.js', array(), $ver );
  642. wp_enqueue_script( 'fl-builder-revisions', $js_url . 'fl-builder-revisions.js', array(), $ver );
  643. wp_enqueue_script( 'fl-builder-search', $js_url . 'fl-builder-search.js', array( 'jquery' ), $ver );
  644. wp_enqueue_script( 'fl-builder-save-manager', $js_url . 'fl-builder-save-manager.js', array( 'jquery' ), $ver );
  645. wp_enqueue_script( 'fl-builder-bundle', $js_url . 'build/builder.bundle.js', array(), $ver, true );
  646. } else {
  647. wp_enqueue_script( 'fl-builder-min', $js_url . 'fl-builder.min.js', array( 'jquery', 'mousetrap' ), $ver );
  648. wp_enqueue_script( 'fl-builder-bundle', $js_url . 'build/builder.bundle.min.js', array(), $ver, true );
  649. }
  650. /* Additional module styles and scripts */
  651. foreach ( FLBuilderModel::$modules as $module ) {
  652. $module->enqueue_scripts();
  653. foreach ( $module->css as $handle => $props ) {
  654. wp_enqueue_style( $handle, $props[0], $props[1], $props[2], $props[3] );
  655. }
  656. foreach ( $module->js as $handle => $props ) {
  657. wp_enqueue_script( $handle, $props[0], $props[1], $props[2], $props[3] );
  658. }
  659. }
  660. }
  661. wp_add_inline_style( 'admin-bar', '#wp-admin-bar-fl-builder-frontend-edit-link .ab-icon:before { content: "\f116" !important; top: 2px; margin-right: 3px; }' );
  662. }
  663. /**
  664. * Include a jQuery fallback script when the builder is
  665. * enabled for a page.
  666. *
  667. * @since 1.0
  668. * @return void
  669. */
  670. static public function include_jquery() {
  671. if ( FLBuilderModel::is_builder_enabled() ) {
  672. include FL_BUILDER_DIR . 'includes/jquery.php';
  673. }
  674. }
  675. /**
  676. * Adds builder classes to the body class.
  677. *
  678. * @since 1.0
  679. * @param array $classes An array of existing classes.
  680. * @return array
  681. */
  682. static public function body_class( $classes ) {
  683. $do_render = apply_filters( 'fl_builder_do_render_content', true, FLBuilderModel::get_post_id() );
  684. $simple_ui = ! FLBuilderUserAccess::current_user_can( 'unrestricted_editing' );
  685. $template_type = FLBuilderModel::get_user_template_type();
  686. if ( $do_render && FLBuilderModel::is_builder_enabled() && ! is_archive() ) {
  687. $classes[] = 'fl-builder';
  688. }
  689. if ( FLBuilderModel::is_builder_active() ) {
  690. $classes[] = 'fl-builder-edit';
  691. // Lite version
  692. if ( true === FL_BUILDER_LITE ) {
  693. $classes[] = 'fl-builder-lite';
  694. }
  695. // Simple UI
  696. if ( $simple_ui ) {
  697. $classes[] = 'fl-builder-simple';
  698. }
  699. // Simple pinned UI
  700. if ( $simple_ui || 'module' === $template_type ) {
  701. $classes[] = 'fl-builder-simple-pinned';
  702. }
  703. // Skin
  704. $user_settings = FLBuilderUserSettings::get();
  705. $classes[] = 'fl-builder-ui-skin--' . $user_settings['skin'];
  706. // Draft changes
  707. if ( FLBuilderModel::layout_has_drafted_changes() ) {
  708. $classes[] = 'fl-builder--layout-has-drafted-changes';
  709. }
  710. // RTL
  711. if ( is_rtl() ) {
  712. $classes[] = 'fl-builder-direction-rtl';
  713. } else {
  714. $classes[] = 'fl-builder-direction-ltr';
  715. }
  716. // Has notifications
  717. $has_new_notifications = FLBuilderNotifications::get_notifications();
  718. if ( ! $has_new_notifications['read'] ) {
  719. $classes[] = 'fl-builder-has-new-notifications';
  720. }
  721. }
  722. return $classes;
  723. }
  724. /**
  725. * Adds the page builder button to the WordPress admin bar.
  726. *
  727. * @since 1.0
  728. * @param object $wp_admin_bar An instance of the WordPress admin bar.
  729. * @return void
  730. */
  731. static public function admin_bar_menu( $wp_admin_bar ) {
  732. global $wp_the_query;
  733. if ( FLBuilderModel::is_post_editable() && is_object( $wp_the_query->post ) ) {
  734. $enabled = get_post_meta( $wp_the_query->post->ID, '_fl_builder_enabled', true );
  735. $dot = ' <span class="fl-builder-admin-bar-status-dot" style="color:' . ( $enabled ? '#6bc373' : '#d9d9d9' ) . '; font-size:18px; line-height:1;">&bull;</span>';
  736. $wp_admin_bar->add_node( array(
  737. 'id' => 'fl-builder-frontend-edit-link',
  738. 'title' => '<span class="ab-icon"></span>' . FLBuilderModel::get_branding() . $dot,
  739. 'href' => FLBuilderModel::get_edit_url( $wp_the_query->post->ID ),
  740. ));
  741. }
  742. }
  743. static public function locate_template_file( $template_base, $slug ) {
  744. $specific_template = $template_base . '-' . $slug . '.php';
  745. $general_template = $template_base . '.php';
  746. $default_dir = trailingslashit( FL_BUILDER_DIR ) . 'includes/';
  747. // Try to find the specific template, then repeat the same process for general.
  748. $locate_template_order = apply_filters( 'fl_builder_locate_template_order', array(
  749. trailingslashit( self::$template_dir ) . $specific_template,
  750. trailingslashit( self::$template_dir ) . $general_template,
  751. ), self::$template_dir, $template_base, $slug );
  752. $template_path = locate_template( $locate_template_order );
  753. if ( ! $template_path ) {
  754. if ( file_exists( $default_dir . $specific_template ) ) {
  755. $template_path = $default_dir . $specific_template;
  756. } elseif ( file_exists( $default_dir . $general_template ) ) {
  757. $template_path = $default_dir . $general_template;
  758. }
  759. }
  760. return apply_filters( 'fl_builder_template_path', $template_path, $template_base, $slug );
  761. }
  762. /**
  763. * Initializes the builder interface.
  764. *
  765. * @since 1.0
  766. * @since 1.8 Method name changed from init to init_ui.
  767. * @return void
  768. */
  769. static public function init_ui() {
  770. // Enable editing if the builder is active.
  771. if ( FLBuilderModel::is_builder_active() && ! FLBuilderAJAX::doing_ajax() ) {
  772. // Tell W3TC not to minify while the builder is active.
  773. define( 'DONOTMINIFY', true );
  774. // Tell Autoptimize not to minify while the builder is active.
  775. add_filter( 'autoptimize_filter_noptimize', '__return_true' );
  776. // Remove 3rd party editor buttons.
  777. remove_all_actions( 'media_buttons', 999999 );
  778. remove_all_actions( 'media_buttons_context', 999999 );
  779. // Increase available memory.
  780. if ( function_exists( 'wp_raise_memory_limit' ) ) {
  781. wp_raise_memory_limit( 'bb-plugin' );
  782. }
  783. // Get the post.
  784. require_once ABSPATH . 'wp-admin/includes/post.php';
  785. $post_id = FLBuilderModel::get_post_id();
  786. // Check to see if the post is locked.
  787. if ( wp_check_post_lock( $post_id ) !== false ) {
  788. header( 'Location: ' . admin_url( '/post.php?post=' . $post_id . '&action=edit' ) );
  789. } else {
  790. FLBuilderModel::enable_editing();
  791. }
  792. }
  793. }
  794. /**
  795. * Renders the markup for the builder interface.
  796. *
  797. * @since 1.0
  798. * @return void
  799. */
  800. static public function render_ui() {
  801. global $wp_the_query;
  802. if ( FLBuilderModel::is_builder_active() ) {
  803. $post_id = is_object( $wp_the_query->post ) ? $wp_the_query->post->ID : null;
  804. $unrestricted = FLBuilderUserAccess::current_user_can( 'unrestricted_editing' );
  805. $simple_ui = ! $unrestricted;
  806. $global_settings = FLBuilderModel::get_global_settings();
  807. include FL_BUILDER_DIR . 'includes/ui-extras.php';
  808. include FL_BUILDER_DIR . 'includes/ui-js-templates.php';
  809. include FL_BUILDER_DIR . 'includes/ui-js-config.php';
  810. }
  811. }
  812. /**
  813. * Get data structure for main builder menu.
  814. *
  815. * @since 2.0
  816. * @return array
  817. */
  818. static function get_main_menu_data() {
  819. global $post;
  820. $views = array();
  821. $is_lite = true === FL_BUILDER_LITE; // @codingStandardsIgnoreLine
  822. $is_user_template = FLBuilderModel::is_post_user_template();
  823. $enabled_templates = FLBuilderModel::get_enabled_templates();
  824. $is_simple_ui = ! FLBuilderUserAccess::current_user_can( 'unrestricted_editing' );
  825. $key_shortcuts = self::get_keyboard_shortcuts();
  826. $help = FLBuilderModel::get_help_button_settings();
  827. $default_view = array(
  828. 'name' => __( 'Unnamed Menu', 'fl-builder' ),
  829. 'isShowing' => false,
  830. 'isRootView' => false,
  831. 'items' => array(),
  832. );
  833. // Tools
  834. $tools_view = array(
  835. 'name' => __( 'Tools', 'fl-builder' ),
  836. 'isShowing' => true,
  837. 'isRootView' => true,
  838. 'items' => array(),
  839. );
  840. if ( ! $is_lite && ! $is_user_template && ( 'enabled' == $enabled_templates || 'user' == $enabled_templates ) ) {
  841. $tools_view['items'][10] = array(
  842. 'label' => __( 'Save Template', 'fl-builder' ),
  843. 'type' => 'event',
  844. 'eventName' => 'saveTemplate',
  845. 'accessory' => $key_shortcuts['saveTemplate']['keyLabel'],
  846. );
  847. }
  848. $tools_view['items'][20] = array(
  849. 'label' => __( 'Duplicate Layout', 'fl-builder' ),
  850. 'type' => 'event',
  851. 'eventName' => 'duplicateLayout',
  852. );
  853. $tools_view['items'][30] = array(
  854. 'label' => __( 'Preview Layout', 'fl-builder' ),
  855. 'type' => 'event',
  856. 'eventName' => 'previewLayout',
  857. 'accessory' => $key_shortcuts['previewLayout']['keyLabel'],
  858. );
  859. $tools_view['items'][40] = array(
  860. 'type' => 'separator',
  861. );
  862. $tools_view['items'][50] = array(
  863. 'label' => __( 'Layout CSS & Javascript', 'fl-builder' ),
  864. 'type' => 'event',
  865. 'eventName' => 'showLayoutSettings',
  866. 'accessory' => $key_shortcuts['showLayoutSettings']['keyLabel'],
  867. );
  868. $tools_view['items'][60] = array(
  869. 'label' => __( 'Global Settings', 'fl-builder' ),
  870. 'type' => 'event',
  871. 'eventName' => 'showGlobalSettings',
  872. 'accessory' => $key_shortcuts['showGlobalSettings']['keyLabel'],
  873. );
  874. $tools_view['items'][70] = array(
  875. 'type' => 'separator',
  876. );
  877. $tools_view['items'][80] = array(
  878. 'label' => __( 'Change UI Brightness', 'fl-builder' ),
  879. 'type' => 'event',
  880. 'eventName' => 'toggleUISkin',
  881. 'accessory' => $key_shortcuts['toggleUISkin']['keyLabel'],
  882. );
  883. $tools_view['items'][100] = array(
  884. 'label' => __( 'WordPress Admin', 'fl-builder' ),
  885. 'type' => 'view',
  886. 'view' => 'admin',
  887. );
  888. if ( $help['enabled'] && ! $is_simple_ui ) {
  889. $tools_view['items'][110] = array(
  890. 'label' => __( 'Help', 'fl-builder' ),
  891. 'type' => 'view',
  892. 'view' => 'help',
  893. );
  894. }
  895. $tools_view['items'][120] = array(
  896. 'label' => __( 'Keyboard Shortcuts', 'fl-builder' ),
  897. 'type' => 'event',
  898. 'eventName' => 'showKeyboardShortcuts',
  899. );
  900. $views['main'] = wp_parse_args( $tools_view, $default_view );
  901. // Admin
  902. $admin_view = array(
  903. 'name' => __( 'WordPress Admin', 'fl-builder' ),
  904. 'items' => array(),
  905. );
  906. // Edit current post/page/cpt
  907. if ( is_single( $post->ID ) || is_page( $post->ID ) ) {
  908. $edit_label = get_post_type_object( $post->post_type )->labels->edit_item;
  909. $admin_view['items'][10] = array(
  910. 'label' => $edit_label,
  911. 'type' => 'link',
  912. 'url' => get_edit_post_link( $post->ID ),
  913. );
  914. }
  915. $admin_view['items'][15] = array(
  916. 'type' => 'separator',
  917. );
  918. // Dashboard
  919. $admin_view['items'][17] = array(
  920. 'label' => _x( 'Dashboard', 'label for the WordPress Dashboard link', 'fl-builder' ),
  921. 'type' => 'link',
  922. 'url' => admin_url( 'index.php' ),
  923. );
  924. $templates_enabled = FLBuilderUserAccess::current_user_can( 'builder_admin' );
  925. if ( $templates_enabled ) {
  926. $admin_view['items'][20] = array(
  927. 'label' => __( 'Manage Templates', 'fl-builder' ),
  928. 'type' => 'link',
  929. 'url' => admin_url( 'edit.php?post_type=fl-builder-template' ),
  930. );
  931. }
  932. if ( current_user_can( 'customize' ) ) {
  933. $post_url = get_permalink( $post->ID );
  934. if ( $post_url ) {
  935. $url = admin_url( 'customize.php?url=' . $post_url );
  936. } else {
  937. $url = admin_url( 'customize.php' );
  938. }
  939. $admin_view['items'][30] = array(
  940. 'label' => __( 'Customize Theme', 'fl-builder' ),
  941. 'type' => 'link',
  942. 'url' => $url,
  943. );
  944. }
  945. $views['admin'] = wp_parse_args( $admin_view, $default_view );
  946. // Help
  947. if ( $help['enabled'] && ! $is_simple_ui ) {
  948. $help_view = array(
  949. 'name' => __( 'Help', 'fl-builder' ),
  950. 'items' => array(),
  951. );
  952. if ( $help['video'] && isset( $help['video_embed'] ) ) {
  953. // Disable Auto Play
  954. $help['video_embed'] = str_replace( 'autoplay=1', 'autoplay=0', $help['video_embed'] );
  955. // Remove Height from iframe
  956. $help['video_embed'] = str_replace( 'height="315"', 'height="173"', $help['video_embed'] );
  957. $help_view['items'][10] = array(
  958. 'type' => 'video',
  959. 'embed' => $help['video_embed'],
  960. );
  961. }
  962. if ( $help['tour'] ) {
  963. $help_view['items'][20] = array(
  964. 'label' => __( 'Take A Tour', 'fl-builder' ),
  965. 'type' => 'event',
  966. 'eventName' => 'beginTour',
  967. );
  968. }
  969. if ( $help['knowledge_base'] && isset( $help['knowledge_base_url'] ) ) {
  970. $help_view['items'][30] = array(
  971. 'label' => __( 'View Knowledge Base', 'fl-builder' ),
  972. 'type' => 'link',
  973. 'url' => $help['knowledge_base_url'],
  974. );
  975. }
  976. if ( $help['forums'] && isset( $help['forums_url'] ) ) {
  977. $help_view['items'][40] = array(
  978. 'label' => __( 'Contact Support', 'fl-builder' ),
  979. 'type' => 'link',
  980. 'url' => $help['forums_url'],
  981. );
  982. }
  983. $views['help'] = wp_parse_args( $help_view, $default_view );
  984. }
  985. return apply_filters( 'fl_builder_main_menu', $views );
  986. }
  987. /**
  988. * Get array of registered keyboard shortcuts. The key corresponds to
  989. * an event to be triggered by FLbuilder.triggerHook()
  990. *
  991. * @since 2.0
  992. * @return array
  993. */
  994. static function get_keyboard_shortcuts() {
  995. $default_action = array(
  996. 'label' => _x( 'Untitled Shortcut', 'A keyboard shortcut with no label given', 'fl-builder' ),
  997. 'keyCode' => '',
  998. 'keyLabel' => '',
  999. 'isGlobal' => false,
  1000. 'enabled' => true,
  1001. );
  1002. $data = array(
  1003. 'showModules' => array(
  1004. 'label' => _x( 'Open Modules Tab', 'Keyboard action to show modules tab', 'fl-builder' ),
  1005. 'keyCode' => 'j',
  1006. ),
  1007. 'showRows' => array(
  1008. 'label' => _x( 'Open Rows Tab', 'Keyboard action to show rows tab', 'fl-builder' ),
  1009. 'keyCode' => 'k',
  1010. ),
  1011. 'showTemplates' => array(
  1012. 'label' => _x( 'Open Templates Tab', 'Keyboard action to show templates tab', 'fl-builder' ),
  1013. 'keyCode' => 'l',
  1014. ),
  1015. 'showSaved' => array(
  1016. 'label' => _x( 'Open Saved Tab', 'Keyboard action to show saved tab', 'fl-builder' ),
  1017. 'keyCode' => ';',
  1018. 'enabled' => true !== FL_BUILDER_LITE,
  1019. ),
  1020. 'saveTemplate' => array(
  1021. 'label' => _x( 'Save New Template', 'Keyboard action to open save template form', 'fl-builder' ),
  1022. 'keyCode' => 'mod+j',
  1023. 'enabled' => true !== FL_BUILDER_LITE,
  1024. ),
  1025. 'previewLayout' => array(
  1026. 'label' => _x( 'Toggle Preview Mode', 'Keyboard action to toggle preview mode', 'fl-builder' ),
  1027. 'keyCode' => 'p',
  1028. ),
  1029. 'showGlobalSettings' => array(
  1030. 'label' => _x( 'Open Global Settings', 'Keyboard action to open the global settings panel', 'fl-builder' ),
  1031. 'keyCode' => 'mod+u',
  1032. ),
  1033. 'showLayoutSettings' => array(
  1034. 'label' => _x( 'Open Layout Settings', 'Keyboard action to open the layout settings panel', 'fl-builder' ),
  1035. 'keyCode' => 'mod+y',
  1036. ),
  1037. 'toggleUISkin' => array(
  1038. 'label' => _x( 'Change UI Brightness', 'Keyboard action to switch between light and dark UI brightness', 'fl-builder' ),
  1039. 'keyCode' => 'o',
  1040. ),
  1041. 'showSearch' => array(
  1042. 'label' => _x( 'Display Module Search', 'Keyboard action to open the module search panel', 'fl-builder' ),
  1043. 'keyCode' => 'mod+i',
  1044. 'enabled' => true !== FL_BUILDER_LITE,
  1045. ),
  1046. 'showSavedMessage' => array(
  1047. 'label' => _x( 'Save Layout', 'Keyboard action to save changes', 'fl-builder' ),
  1048. 'keyCode' => 'mod+s',
  1049. 'isGlobal' => true,
  1050. ),
  1051. 'publishAndRemain' => array(
  1052. 'label' => _x( 'Publish changes without leaving builder', 'Keyboard action to publish any pending changes', 'fl-builder' ),
  1053. 'keyCode' => 'mod+p',
  1054. 'isGlobal' => true,
  1055. ),
  1056. 'cancelTask' => array(
  1057. 'label' => _x( 'Dismiss Active Panel', 'Keyboard action to dismiss the current task or panel', 'fl-builder' ),
  1058. 'keyCode' => 'esc',
  1059. 'isGlobal' => true,
  1060. ),
  1061. );
  1062. $data = apply_filters( 'fl_builder_keyboard_shortcuts', $data );
  1063. foreach ( $data as $hook => $args ) {
  1064. // Check for old (alpha) format and normalize
  1065. if ( is_string( $args ) ) {
  1066. $args = array(
  1067. 'label' => ucwords( preg_replace( '/([^A-Z])([A-Z])/', '$1 $2', $hook ) ),
  1068. 'keyCode' => $args,
  1069. );
  1070. }
  1071. $args = wp_parse_args( $args, $default_action );
  1072. // Unset this shortcut if it's not enabled.
  1073. if ( ! $args['enabled'] ) {
  1074. unset( $data[ $hook ] );
  1075. continue;
  1076. }
  1077. // Map 'mod' to mac or pc equivalent
  1078. $code = $args['keyCode'];
  1079. $code = str_replace( '+', '', $code );
  1080. if ( false !== strpos( $code, 'mod' ) ) {
  1081. $is_mac = strpos( $_SERVER['HTTP_USER_AGENT'], 'Macintosh' ) ? true : false;
  1082. if ( $is_mac ) {
  1083. $code = str_replace( 'mod', 'command', $code );
  1084. } else {
  1085. $code = str_replace( 'mod', 'Ctrl+', $code );
  1086. }
  1087. }
  1088. // Replace 'command'
  1089. $code = str_replace( 'command', '&#8984;', $code );
  1090. // Replace 'shift'
  1091. $code = str_replace( 'shift', '&#x21E7;', $code );
  1092. // Replace 'delete'
  1093. $code = str_replace( 'delete', '&#x232b;', $code );
  1094. // Replace 'left' arrow
  1095. $code = str_replace( 'left', '&larr;', $code );
  1096. // Replace 'right' arrow
  1097. $code = str_replace( 'right', '&rarr;', $code );
  1098. $args['keyLabel'] = $code;
  1099. $data[ $hook ] = $args;
  1100. }
  1101. return $data;
  1102. }
  1103. /**
  1104. * Renders the markup for the title in the builder's bar.
  1105. *
  1106. * @since 1.6.3
  1107. * @return void
  1108. */
  1109. static public function render_ui_bar_title() {
  1110. global $post;
  1111. $simple_ui = ! FLBuilderUserAccess::current_user_can( 'unrestricted_editing' );
  1112. $title = apply_filters( 'fl_builder_ui_bar_title', get_the_title( $post->ID ) );
  1113. $icon_url = FLBuilderModel::get_branding_icon();
  1114. $wrapper_classes = array( 'fl-builder-bar-title' );
  1115. if ( '' == $icon_url ) {
  1116. $wrapper_classes[] = 'fl-builder-bar-title-no-icon';
  1117. }
  1118. $edited_object_label = get_post_type_object( $post->post_type )->labels->singular_name;
  1119. $pretitle = sprintf( _x( 'Currently Editing %s', 'Currently editing message', 'fl-builder' ), $edited_object_label );
  1120. $pretitle = apply_filters( 'fl_builder_ui_bar_pretitle', $pretitle );
  1121. // Render the bar title.
  1122. include FL_BUILDER_DIR . 'includes/ui-bar-title-area.php';
  1123. }
  1124. /**
  1125. * Renders the markup for the buttons in the builder's bar.
  1126. *
  1127. * @since 1.6.3
  1128. * @return void
  1129. */
  1130. static public function render_ui_bar_buttons() {
  1131. $help_button = FLBuilderModel::get_help_button_settings();
  1132. $simple_ui = ! FLBuilderUserAccess::current_user_can( 'unrestricted_editing' );
  1133. $should_display_search = ! FLBuilderModel::is_post_user_template( 'module' ) && ! $simple_ui;
  1134. $add_btn_svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="24" height="24"><rect x="0" fill="none" width="24" height="24" /><g><path d="M17 9v2h-6v6H9v-6H3V9h6V3h2v6h6z"/></g></svg>';
  1135. $notifications = FLBuilderNotifications::get_notifications();
  1136. $buttons = apply_filters( 'fl_builder_ui_bar_buttons', array(
  1137. 'upgrade' => array(
  1138. 'label' => __( 'Upgrade Today', 'fl-builder' ) . ' <i class="fas fa-external-link-alt"></i>',
  1139. 'show' => true === FL_BUILDER_LITE,
  1140. ),
  1141. 'buy' => array(
  1142. 'label' => __( 'Buy Now', 'fl-builder' ) . ' <i class="fas fa-external-link-alt"></i>',
  1143. 'show' => stristr( home_url(), 'demo.wpbeaverbuilder.com' ),
  1144. ),
  1145. 'done' => array(
  1146. 'label' => __( 'Done', 'fl-builder' ),
  1147. ),
  1148. 'content-panel' => array(
  1149. 'label' => $add_btn_svg,
  1150. 'show' => ! $simple_ui,
  1151. ),
  1152. 'add-content' => array( // Only added here for backwards compat.
  1153. 'label' => $add_btn_svg,
  1154. 'show' => ! $simple_ui,
  1155. ),
  1156. ) );
  1157. echo '<div class="fl-builder-bar-actions">';
  1158. $i = 0;
  1159. foreach ( $buttons as $slug => $button ) {
  1160. if ( 'add-content' == $slug ) {
  1161. continue; // The old add content button is no longer supported.
  1162. }
  1163. if ( isset( $button['show'] ) && ! $button['show'] ) {
  1164. continue;
  1165. }
  1166. echo '<button class="fl-builder-' . $slug . '-button fl-builder-button';
  1167. if ( isset( $button['class'] ) ) {
  1168. echo ' ' . $button['class'];
  1169. }
  1170. echo '">' . $button['label'] . '</button>';
  1171. $i++;
  1172. }
  1173. echo '<span class="fl-builder--saving-indicator"></span>';
  1174. if ( ! $simple_ui && ! FLBuilderModel::is_white_labeled() && $notifications['data'] && '{}' !== $notifications['data'] && ! apply_filters( 'fl_disable_notifications', false ) ) {
  1175. echo '<span class="fl-builder-bar-spacer"></span>';
  1176. echo '<button id="fl-builder-toggle-notifications" class="fl-builder-button fl-builder-button-silent">';
  1177. include FL_BUILDER_DIR . 'img/svg/bell-active.svg';
  1178. echo '</button>';
  1179. }
  1180. echo '</div>';
  1181. }
  1182. /**
  1183. * Renders layouts using a new instance of WP_Query with the provided
  1184. * args and enqueues the necessary styles and scripts. We set the global
  1185. * $wp_query variable so the builder thinks we are in the loop when content
  1186. * is rendered without having to call query_posts.
  1187. *
  1188. * @link https://codex.wordpress.org/Class_Reference/WP_Query See for a complete list of args.
  1189. *
  1190. * @since 1.7
  1191. * @param array|string $args An array or string of args to be passed to a new instance of WP_Query.
  1192. * @param int $site_id The ID of a site on a network to pull the query from.
  1193. * @return void
  1194. */
  1195. static public function render_query( $args, $site_id = null ) {
  1196. global $post;
  1197. $switched = false;
  1198. // Pull from a site on the network?
  1199. if ( $site_id && is_multisite() ) {
  1200. switch_to_blog( $site_id );
  1201. $switched = true;
  1202. }
  1203. // Get the query.
  1204. $query = new WP_Query( $args );
  1205. // Loop through the posts.
  1206. foreach ( $query->posts as $query_post ) {
  1207. // Make sure this isn't the same post as the original post to prevent infinite loops.
  1208. if ( is_object( $post ) && $post->ID === $query_post->ID && ! $switched ) {
  1209. continue;
  1210. }
  1211. if ( FLBuilderModel::is_builder_enabled( $query_post->ID ) ) {
  1212. // Enqueue styles and scripts for this post.
  1213. self::enqueue_layout_styles_scripts_by_id( $query_post->ID );
  1214. // Print the styles if we are outside of the head tag.
  1215. if ( ! doing_action( 'wp_enqueue_scripts' ) ) {
  1216. wp_print_styles();
  1217. }
  1218. // Render the builder content.
  1219. FLBuilder::render_content_by_id( $query_post->ID );
  1220. } else {
  1221. // Render the WP editor content if the builder isn't enabled.
  1222. echo apply_filters( 'the_content', $query_post->post_content );
  1223. }
  1224. }
  1225. // Reset the site data?
  1226. if ( $site_id && is_multisite() ) {
  1227. restore_current_blog();
  1228. }
  1229. }
  1230. /**
  1231. * Renders the layout for a post with the given post ID.
  1232. * This is useful for rendering builder content outside
  1233. * of the loop.
  1234. *
  1235. * @since 1.10
  1236. * @param int $post_id The ID of the post with the layout to render.
  1237. * @param string $tag The HTML tag for the content wrapper.
  1238. * @param array $attrs An array of key/value attribute data for the content wrapper.
  1239. * @return void
  1240. */
  1241. static public function render_content_by_id( $post_id, $tag = 'div', $attrs = array() ) {
  1242. // Force the builder to use this post ID.
  1243. FLBuilderModel::set_post_id( $post_id );
  1244. // Build the attributes string.
  1245. $attr_string = '';
  1246. foreach ( $attrs as $attr_key => $attr_value ) {
  1247. $attr_string .= ' ' . $attr_key . '="' . $attr_value . '"';
  1248. }
  1249. // Prevent the builder's render_content filter from running.
  1250. add_filter( 'fl_builder_do_render_content', '__return_false' );
  1251. // Fire the render content start action.
  1252. do_action( 'fl_builder_render_content_start' );
  1253. // Render the content.
  1254. ob_start();
  1255. do_action( 'fl_builder_before_render_content' );
  1256. echo '<' . $tag . ' class="' . self::render_content_classes() . '" data-post-id="' . $post_id . '"' . $attr_string . '>';
  1257. self::render_nodes();
  1258. echo '</' . $tag . '>';
  1259. do_action( 'fl_builder_after_render_content' );
  1260. $content = ob_get_clean();
  1261. // Allow the builder's render_content filter to run again.
  1262. remove_filter( 'fl_builder_do_render_content', '__return_false' );
  1263. // Process shortcodes.
  1264. if ( apply_filters( 'fl_builder_render_shortcodes', true ) ) {
  1265. global $wp_embed;
  1266. $content = apply_filters( 'fl_builder_before_render_shortcodes', $content );
  1267. $pattern = get_shortcode_regex();
  1268. $content = preg_replace_callback( "/$pattern/s", 'FLBuilder::double_escape_shortcodes', $content );
  1269. $content = $wp_embed->run_shortcode( $content );
  1270. $content = do_shortcode( $content );
  1271. }
  1272. // Add srcset attrs to images with the class wp-image-<ID>.
  1273. if ( function_exists( 'wp_make_content_images_responsive' ) ) {
  1274. $content = wp_make_content_images_responsive( $content );
  1275. }
  1276. // Fire the render content complete action.
  1277. do_action( 'fl_builder_render_content_complete' );
  1278. // Stop forcing the builder to use this post ID.
  1279. FLBuilderModel::reset_post_id();
  1280. echo $content;
  1281. }
  1282. /**
  1283. * Renders the content for a builder layout while in the loop.
  1284. * This method should only be called by the_content filter as
  1285. * defined in this class. To output builder content, use
  1286. * the_content function while in a WordPress loop or use
  1287. * the FLBuilder::render_content_by_id method.
  1288. *
  1289. * @since 1.0
  1290. * @param string $content The existing content.
  1291. * @return string
  1292. */
  1293. static public function render_content( $content ) {
  1294. $post_id = FLBuilderModel::get_post_id( true );
  1295. $enabled = FLBuilderModel::is_builder_enabled( $post_id );
  1296. $rendering = $post_id === self::$post_rendering;
  1297. $do_render = apply_filters( 'fl_builder_do_render_content', true, $post_id );
  1298. $in_loop = in_the_loop();
  1299. $is_global = in_array( $post_id, FLBuilderModel::get_global_posts() );
  1300. if ( $enabled && ! $rendering && $do_render && ( $in_loop || $is_global ) ) {
  1301. // Set the post rendering ID.
  1302. self::$post_rendering = $post_id;
  1303. // Try to enqueue here in case it didn't happen in the head for this layout.
  1304. self::enqueue_layout_styles_scripts();
  1305. // Render the content.
  1306. ob_start();
  1307. self::render_content_by_id( $post_id );
  1308. $content = ob_get_clean();
  1309. // Clear the post rendering ID.
  1310. self::$post_rendering = null;
  1311. }
  1312. return $content;
  1313. }
  1314. /**
  1315. * Escaped shortcodes need to be double escaped or they will
  1316. * be parsed by WP's shortcodes filter.
  1317. *
  1318. * @since 1.6.4.1
  1319. * @param array $matches The existing content.
  1320. * @return string
  1321. */
  1322. static public function double_escape_shortcodes( $matches ) {
  1323. if ( '[' == $matches[1] && ']' == $matches[6] ) {
  1324. return '[' . $matches[0] . ']';
  1325. }
  1326. return $matches[0];
  1327. }
  1328. /**
  1329. * Renders the CSS classes for the main content div tag.
  1330. *
  1331. * @since 1.6.4
  1332. * @return string
  1333. */
  1334. static public function render_content_classes() {
  1335. global $wp_the_query;
  1336. $post_id = FLBuilderModel::get_post_id();
  1337. // Build the content class.
  1338. $classes = 'fl-builder-content fl-builder-content-' . $post_id;
  1339. // Add the primary content class.
  1340. if ( isset( $wp_the_query->post ) && $wp_the_query->post->ID == $post_id ) {
  1341. $classes .= ' fl-builder-content-primary';
  1342. }
  1343. // Add browser specific classes.
  1344. if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
  1345. if ( stristr( $_SERVER['HTTP_USER_AGENT'], 'Trident/7.0' ) && stristr( $_SERVER['HTTP_USER_AGENT'], 'rv:11.0' ) ) {
  1346. $classes .= ' fl-builder-ie-11';
  1347. }
  1348. }
  1349. return apply_filters( 'fl_builder_content_classes', $classes );
  1350. }
  1351. /**
  1352. * Renders the markup for all nodes in a layout.
  1353. *
  1354. * @since 1.6.3
  1355. * @return void
  1356. */
  1357. static public function render_nodes() {
  1358. do_action( 'fl_builder_before_render_nodes' );
  1359. if ( apply_filters( 'fl_builder_render_nodes', true ) ) {
  1360. self::render_rows();
  1361. }
  1362. do_action( 'fl_builder_after_render_nodes' );
  1363. }
  1364. /**
  1365. * Renders the markup for a node's attributes.
  1366. *
  1367. * @since 1.8
  1368. * @param array $attrs
  1369. * @return void
  1370. */
  1371. static public function render_node_attributes( $attrs ) {
  1372. foreach ( $attrs as $attr_key => $attr_value ) {
  1373. if ( empty( $attr_value ) ) {
  1374. continue;
  1375. } elseif ( is_string( $attr_value ) ) {
  1376. echo ' ' . $attr_key . '="' . $attr_value . '"';
  1377. } elseif ( is_array( $attr_value ) && ! empty( $attr_value ) ) {
  1378. echo ' ' . $attr_key . '="';
  1379. for ( $i = 0; $i < count( $attr_value ); $i++ ) {
  1380. echo $attr_value[ $i ];
  1381. if ( $i < count( $attr_value ) - 1 ) {
  1382. echo ' ';
  1383. }
  1384. }
  1385. echo '"';
  1386. }
  1387. }
  1388. }
  1389. /**
  1390. * Renders the stripped down content for a layout
  1391. * that is saved to the WordPress editor.
  1392. *
  1393. * @since 1.0
  1394. * @param string $content The existing content.
  1395. * @return string
  1396. */
  1397. static public function render_editor_content() {
  1398. $rows = FLBuilderModel::get_nodes( 'row' );
  1399. ob_start();
  1400. // Render the modules.
  1401. foreach ( $rows as $row ) {
  1402. $groups = FLBuilderModel::get_nodes( 'column-group', $row );
  1403. foreach ( $groups as $group ) {
  1404. $cols = FLBuilderModel::get_nodes( 'column', $group );
  1405. foreach ( $cols as $col ) {
  1406. $col_children = FLBuilderModel::get_nodes( null, $col );
  1407. foreach ( $col_children as $col_child ) {
  1408. if ( 'module' == $col_child->type ) {
  1409. $module = FLBuilderModel::get_module( $col_child );
  1410. if ( $module && $module->editor_export ) {
  1411. // Don't crop photos to ensure media library photos are rendered.
  1412. if ( 'photo' == $module->settings->type ) {
  1413. $module->settings->crop = false;
  1414. }
  1415. FLBuilder::render_module_html( $module->settings->type, $module->settings, $module );
  1416. }
  1417. } elseif ( 'column-group' == $col_child->type ) {
  1418. $group_cols = FLBuilderModel::get_nodes( 'column', $col_child );
  1419. foreach ( $group_cols as $group_col ) {
  1420. $modules = FLBuilderModel::get_modules( $group_col );
  1421. foreach ( $modules as $module ) {
  1422. if ( $module->editor_export ) {
  1423. // Don't crop photos to ensure media library photos are rendered.
  1424. if ( 'photo' == $module->settings->type ) {
  1425. $module->settings->crop = false;
  1426. }
  1427. FLBuilder::render_module_html( $module->settings->type, $module->settings, $module );
  1428. }
  1429. }
  1430. }
  1431. }
  1432. }
  1433. }
  1434. }
  1435. }
  1436. // Get the content.
  1437. $content = ob_get_clean();
  1438. // Remove unnecessary tags and attributes.
  1439. $content = preg_replace( '/<\/?div[^>]*\>/i', '', $content );
  1440. $content = preg_replace( '/<\/?span[^>]*\>/i', '', $content );
  1441. $content = preg_replace( '#<script(.*?)>(.*?)</script>#is', '', $content );
  1442. $content = preg_replace( '/<\/?noscript[^>]*\>/i', '', $content );
  1443. $content = preg_replace( '#<svg(.*?)>(.*?)</svg>#is', '', $content );
  1444. $content = preg_replace( '/<i [^>]*><\\/i[^>]*>/', '', $content );
  1445. $content = preg_replace( '/ class=".*?"/', '', $content );
  1446. $content = preg_replace( '/ style=".*?"/', '', $content );
  1447. // Remove empty lines.
  1448. $content = preg_replace( '/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/', "\n", $content );
  1449. return apply_filters( 'fl_builder_editor_content', $content );
  1450. }
  1451. /**
  1452. * Renders a settings via PHP. This method is only around for
  1453. * backwards compatibility with third party settings forms that are
  1454. * still being rendered via AJAX. Going forward, all settings forms
  1455. * should be rendered on the frontend using FLBuilderSettingsForms.render.
  1456. *
  1457. * @since 1.0
  1458. * @param array $form The form data.
  1459. * @param object $settings The settings data.
  1460. * @return array
  1461. */
  1462. static public function render_settings( $form = array(), $settings ) {
  1463. return FLBuilderUISettingsForms::render_settings( $form, $settings );
  1464. }
  1465. /**
  1466. * Renders a settings form via PHP. This method is only around for
  1467. * backwards compatibility with third party settings forms that are
  1468. * still being rendered via AJAX. Going forward, all settings forms
  1469. * should be rendered on the frontend using FLBuilderSettingsForms.render.
  1470. *
  1471. * @since 1.0
  1472. * @param string $type The type of form to render.
  1473. * @param object $settings The settings data.
  1474. * @return array
  1475. */
  1476. static public function render_settings_form( $type = null, $settings = null ) {
  1477. return FLBuilderUISettingsForms::render_settings_form( $type, $settings );
  1478. }
  1479. /**
  1480. * Renders a settings field via PHP. This method is only around for
  1481. * backwards compatibility with third party settings forms that are
  1482. * still being rendered via AJAX. Going forward, all settings forms
  1483. * should be rendered on the frontend using FLBuilderSettingsForms.render.
  1484. *
  1485. * @since 1.0
  1486. * @param string $name The field name.
  1487. * @param array $field An array of setup data for the field.
  1488. * @param object $settings Form settings data object.
  1489. * @return void
  1490. */
  1491. static public function render_settings_field( $name, $field, $settings = null ) {
  1492. return FLBuilderUISettingsForms::render_settings_field( $name, $field, $settings );
  1493. }
  1494. /**
  1495. * Renders the markup for the icon selector.
  1496. *
  1497. * @since 1.0
  1498. * @return array
  1499. */
  1500. static public function render_icon_selector() {
  1501. return FLBuilderUISettingsForms::render_icon_selector();
  1502. }
  1503. /**
  1504. * Renders the markup for all of the rows in a layout.
  1505. *
  1506. * @since 1.0
  1507. * @return void
  1508. */
  1509. static public function render_rows() {
  1510. $rows = FLBuilderModel::get_nodes( 'row' );
  1511. do_action( 'fl_builder_before_render_rows', $rows );
  1512. foreach ( $rows as $row ) {
  1513. self::render_row( $row );
  1514. }
  1515. do_action( 'fl_builder_after_render_rows', $rows );
  1516. }
  1517. /**
  1518. * Renders the markup for a single row.
  1519. *
  1520. * @since 1.0
  1521. * @param object $row The row to render.
  1522. * @return void
  1523. */
  1524. static public function render_row( $row ) {
  1525. global $wp_the_query;
  1526. $groups = FLBuilderModel::get_nodes( 'column-group', $row );
  1527. $post_id = FLBuilderModel::get_post_id();
  1528. $active = FLBuilderModel::is_builder_active() && $post_id == $wp_the_query->post->ID;
  1529. $visible = FLBuilderModel::is_node_visible( $row );
  1530. if ( $active || $visible ) {
  1531. do_action( 'fl_builder_before_render_row', $row, $groups );
  1532. $template_file = self::locate_template_file(
  1533. apply_filters( 'fl_builder_row_template_base', 'row', $row ),
  1534. apply_filters( 'fl_builder_row_template_slug', '', $row )
  1535. );
  1536. if ( $template_file ) {
  1537. include $template_file;
  1538. }
  1539. do_action( 'fl_builder_after_render_row', $row, $groups );
  1540. }
  1541. }
  1542. /**
  1543. * Renders the HTML attributes for a single row.
  1544. *
  1545. * @since 1.0
  1546. * @param object $row A row node object.
  1547. * @return void
  1548. */
  1549. static public function render_row_attributes( $row ) {
  1550. /**
  1551. * Use this filter to work with the custom class a user adds to a row under Row Settings > Advanced > Class.
  1552. * @see fl_builder_row_custom_class
  1553. * @link https://kb.wpbeaverbuilder.com/article/117-plugin-filter-reference
  1554. */
  1555. $custom_class = apply_filters( 'fl_builder_row_custom_class', $row->settings->class, $row );
  1556. $overlay_bgs = array( 'photo', 'parallax', 'slideshow', 'video' );
  1557. $active = FLBuilderModel::is_builder_active();
  1558. $visible = FLBuilderModel::is_node_visible( $row );
  1559. $has_rules = FLBuilderModel::node_has_visibility_rules( $row );
  1560. $attrs = array(
  1561. 'id' => $row->settings->id,
  1562. 'class' => array(
  1563. 'fl-row',
  1564. 'fl-row-' . $row->settings->width . '-width',
  1565. 'fl-row-bg-' . $row->settings->bg_type,
  1566. 'fl-node-' . $row->node,
  1567. ),
  1568. 'data-node' => $row->node,
  1569. );
  1570. // Classes
  1571. if ( ! empty( $row->settings->full_height ) && 'full' == $row->settings->full_height ) {
  1572. $attrs['class'][] = 'fl-row-full-height';
  1573. if ( isset( $row->settings->content_alignment ) ) {
  1574. $attrs['class'][] = 'fl-row-align-' . $row->settings->content_alignment;
  1575. }
  1576. }
  1577. if ( in_array( $row->settings->bg_type, $overlay_bgs ) && ! empty( $row->settings->bg_overlay_color ) ) {
  1578. $attrs['class'][] = 'fl-row-bg-overlay';
  1579. }
  1580. if ( ! empty( $row->settings->responsive_display ) ) {
  1581. $attrs['class'][] = 'fl-visible-' . $row->settings->responsive_display;
  1582. }
  1583. if ( ! empty( $custom_class ) ) {
  1584. $attrs['class'][] = trim( esc_attr( $custom_class ) );
  1585. }
  1586. if ( $active && ! $visible ) {
  1587. $attrs['class'][] = 'fl-node-hidden';
  1588. }
  1589. if ( $active && $has_rules ) {
  1590. $attrs['class'][] = 'fl-node-has-rules';
  1591. }
  1592. // Data
  1593. if ( 'parallax' == $row->settings->bg_type && ! empty( $row->settings->bg_parallax_image_src ) ) {
  1594. $attrs['data-parallax-speed'] = $row->settings->bg_parallax_speed;
  1595. $attrs['data-parallax-image'] = $row->settings->bg_parallax_image_src;
  1596. }
  1597. self::render_node_attributes( apply_filters( 'fl_builder_row_attributes', $attrs, $row ) );
  1598. }
  1599. /**
  1600. * Renders the markup for a row's background.
  1601. *
  1602. * @since 1.0
  1603. * @param object $row A row node object.
  1604. * @return void
  1605. */
  1606. static public function render_row_bg( $row ) {
  1607. do_action( 'fl_builder_before_render_row_bg', $row );
  1608. if ( 'video' == $row->settings->bg_type ) {
  1609. $vid_data = FLBuilderModel::get_row_bg_data( $row );
  1610. if ( $vid_data || in_array( $row->settings->bg_video_source, array( 'video_url', 'video_service' ) ) ) {
  1611. $template_file = self::locate_template_file(
  1612. apply_filters( 'fl_builder_row_video_bg_template_base', 'row-video', $row ),
  1613. apply_filters( 'fl_builder_row_video_bg_template_slug', '', $row )
  1614. );
  1615. if ( $template_file ) {
  1616. include $template_file;
  1617. }
  1618. }
  1619. } elseif ( 'slideshow' == $row->settings->bg_type ) {
  1620. echo '<div class="fl-bg-slideshow"></div>';
  1621. }
  1622. do_action( 'fl_builder_after_render_row_bg', $row );
  1623. }
  1624. /**
  1625. * Renders the HTML class for a row's content wrapper.
  1626. *
  1627. * @since 1.0
  1628. * @param object $row A row node object.
  1629. * @return void
  1630. */
  1631. static public function render_row_content_class( $row ) {
  1632. echo 'fl-row-content';
  1633. echo ' fl-row-' . $row->settings->content_width . '-width';
  1634. echo ' fl-node-content';
  1635. }
  1636. /**
  1637. * Renders the markup for a column group.
  1638. *
  1639. * @since 1.0
  1640. * @param object $group A column group node object.
  1641. * @return void
  1642. */
  1643. static public function render_column_group( $group ) {
  1644. $cols = FLBuilderModel::get_nodes( 'column', $group );
  1645. do_action( 'fl_builder_before_render_column_group', $group, $cols );
  1646. $template_file = self::locate_template_file(
  1647. apply_filters( 'fl_builder_column_group_template_base', 'column-group', $group ),
  1648. apply_filters( 'fl_builder_column_group_template_slug', '', $group )
  1649. );
  1650. if ( $template_file ) {
  1651. include $template_file;
  1652. }
  1653. do_action( 'fl_builder_after_render_column_group', $group, $cols );
  1654. }
  1655. /**
  1656. * Renders the attrs for a column group.
  1657. *
  1658. * @since 1.0
  1659. * @param object $group
  1660. * @return void
  1661. */
  1662. static public function render_column_group_attributes( $group ) {
  1663. $cols = FLBuilderModel::get_nodes( 'column', $group );
  1664. $parent = FLBuilderModel::get_node_parent( $group );
  1665. $attrs = array(
  1666. 'class' => array(
  1667. 'fl-col-group',
  1668. 'fl-node-' . $group->node,
  1669. ),
  1670. 'data-node' => $group->node,
  1671. );
  1672. if ( 'column' == $parent->type ) {
  1673. $attrs['class'][] = 'fl-col-group-nested';
  1674. }
  1675. foreach ( $cols as $col ) {
  1676. if ( isset( $col->settings->equal_height ) && 'yes' == $col->settings->equal_height ) {
  1677. if ( ! in_array( 'fl-col-group-equal-height', $attrs['class'] ) ) {
  1678. $attrs['class'][] = 'fl-col-group-equal-height';
  1679. }
  1680. if ( isset( $col->settings->content_alignment ) ) {
  1681. if ( ! in_array( 'fl-col-group-align-' . $col->settings->content_alignment, $attrs['class'] ) ) {
  1682. $attrs['class'][] = 'fl-col-group-align-' . $col->settings->content_alignment;
  1683. }
  1684. }
  1685. }
  1686. if ( isset( $col->settings->responsive_size ) && 'custom' == $col->settings->responsive_size ) {
  1687. if ( ! in_array( 'fl-col-group-custom-width', $attrs['class'] ) ) {
  1688. $attrs['class'][] = 'fl-col-group-custom-width';
  1689. }
  1690. }
  1691. if ( isset( $col->settings->responsive_order ) && 'reversed' == $col->settings->responsive_order ) {
  1692. if ( ! in_array( 'fl-col-group-responsive-reversed', $attrs['class'] ) ) {
  1693. $attrs['class'][] = 'fl-col-group-responsive-reversed';
  1694. }
  1695. }
  1696. }
  1697. self::render_node_attributes( apply_filters( 'fl_builder_column_group_attributes', $attrs, $group ) );
  1698. }
  1699. /**
  1700. * Renders the markup for a single column.
  1701. *
  1702. * @since 1.7
  1703. * @param string|object $col_id A column ID or object.
  1704. * @return void
  1705. */
  1706. static public function render_column( $col_id = null ) {
  1707. global $wp_the_query;
  1708. $col = is_object( $col_id ) ? $col_id : FLBuilderModel::get_node( $col_id );
  1709. $post_id = FLBuilderModel::get_post_id();
  1710. $active = FLBuilderModel::is_builder_active() && $post_id == $wp_the_query->post->ID;
  1711. $visible = FLBuilderModel::is_node_visible( $col );
  1712. if ( $active || $visible ) {
  1713. include FL_BUILDER_DIR . 'includes/column.php';
  1714. }
  1715. }
  1716. /**
  1717. * Renders the HTML attributes for a single column.
  1718. *
  1719. * @since 1.0
  1720. * @param object $col A column node object.
  1721. * @return void
  1722. */
  1723. static public function render_column_attributes( $col ) {
  1724. /**
  1725. * Use this filter to work with the custom class a user adds to a column under Column Settings > Advanced > Class.
  1726. * @see fl_builder_column_custom_class
  1727. * @link https://kb.wpbeaverbuilder.com/article/117-plugin-filter-reference
  1728. */
  1729. $custom_class = apply_filters( 'fl_builder_column_custom_class', $col->settings->class, $col );
  1730. $overlay_bgs = array( 'photo' );
  1731. $nested = FLBuilderModel::get_nodes( 'column-group', $col );
  1732. $active = FLBuilderModel::is_builder_active();
  1733. $visible = FLBuilderModel::is_node_visible( $col );
  1734. $has_rules = FLBuilderModel::node_has_visibility_rules( $col );
  1735. $attrs = array(
  1736. 'id' => $col->settings->id,
  1737. 'class' => array(
  1738. 'fl-col',
  1739. 'fl-node-' . $col->node,
  1740. ),
  1741. 'data-node' => $col->node,
  1742. 'style' => array(),
  1743. );
  1744. // Classes
  1745. if ( $col->settings->size <= 50 ) {
  1746. $attrs['class'][] = 'fl-col-small';
  1747. }
  1748. if ( count( $nested ) > 0 ) {
  1749. $attrs['class'][] = 'fl-col-has-cols';
  1750. }
  1751. if ( in_array( $col->settings->bg_type, $overlay_bgs ) && ! empty( $col->settings->bg_overlay_color ) ) {
  1752. $attrs['class'][] = 'fl-col-bg-overlay';
  1753. }
  1754. if ( ! empty( $col->settings->responsive_display ) ) {
  1755. $attrs['class'][] = 'fl-visible-' . $col->settings->responsive_display;
  1756. }
  1757. if ( ! empty( $custom_class ) ) {
  1758. $attrs['class'][] = trim( esc_attr( $custom_class ) );
  1759. }
  1760. if ( $active && ! $visible ) {
  1761. $attrs['class'][] = 'fl-node-hidden';
  1762. }
  1763. if ( $active && $has_rules ) {
  1764. $attrs['class'][] = 'fl-node-has-rules';
  1765. }
  1766. // Style
  1767. if ( $active ) {
  1768. $attrs['style'][] = 'width: ' . $col->settings->size . '%;';
  1769. }
  1770. // Render the attrs
  1771. self::render_node_attributes( apply_filters( 'fl_builder_column_attributes', $attrs, $col ) );
  1772. }
  1773. /**
  1774. * Renders the markup for all modules in a column.
  1775. *
  1776. * @since 1.0
  1777. * @param string|object $col_id A column ID or object.
  1778. * @return void
  1779. */
  1780. static public function render_modules( $col_id = null ) {
  1781. $nodes = FLBuilderModel::get_nodes( null, $col_id );
  1782. do_action( 'fl_builder_before_render_modules', $nodes, $col_id );
  1783. foreach ( $nodes as $node ) {
  1784. if ( 'module' == $node->type && FLBuilderModel::is_module_registered( $node->settings->type ) ) {
  1785. self::render_module( $node );
  1786. } elseif ( 'column-group' == $node->type ) {
  1787. self::render_column_group( $node );
  1788. }
  1789. }
  1790. do_action( 'fl_builder_after_render_modules', $nodes, $col_id );
  1791. }
  1792. /**
  1793. * Renders the markup for a single module.
  1794. *
  1795. * @since 1.7
  1796. * @param string|object $module_id A module ID or object.
  1797. * @return void
  1798. */
  1799. static public function render_module( $module_id = null ) {
  1800. global $wp_the_query;
  1801. $module = FLBuilderModel::get_module( $module_id );
  1802. $settings = $module->settings;
  1803. $id = $module->node;
  1804. $post_id = FLBuilderModel::get_post_id();
  1805. $active = FLBuilderModel::is_builder_active() && $post_id == $wp_the_query->post->ID;
  1806. $visible = FLBuilderModel::is_node_visible( $module );
  1807. if ( $active || $visible ) {
  1808. do_action( 'fl_builder_before_render_module', $module );
  1809. $template_file = self::locate_template_file(
  1810. apply_filters( 'fl_builder_module_template_base', 'module', $module ),
  1811. apply_filters( 'fl_builder_module_template_slug', '', $module )
  1812. );
  1813. if ( $template_file ) {
  1814. include $template_file;
  1815. }
  1816. do_action( 'fl_builder_after_render_module', $module );
  1817. }
  1818. }
  1819. /**
  1820. * Renders the markup for a single module. This can be used to render
  1821. * the markup of a module within another module by passing the type
  1822. * and settings params and leaving the module param null.
  1823. *
  1824. * @since 1.0
  1825. * @param string $type The type of module.
  1826. * @param object $settings A module settings object.
  1827. * @param object $module Optional. An existing module object to use.
  1828. * @return void
  1829. */
  1830. static public function render_module_html( $type, $settings, $module = null ) {
  1831. // Settings
  1832. $defaults = FLBuilderModel::get_module_defaults( $type );
  1833. $settings = (object) array_merge( (array) $defaults, (array) $settings );
  1834. // Module
  1835. $class = get_class( FLBuilderModel::$modules[ $type ] );
  1836. $module = new $class();
  1837. $module->settings = $settings;
  1838. // Shorthand reference to the module's id.
  1839. $id = $module->node;
  1840. do_action( 'fl_builder_render_module_html_before', $type, $settings, $module );
  1841. ob_start();
  1842. include apply_filters( 'fl_builder_render_module_html', $module->dir . 'includes/frontend.php', $type, $settings, $module );
  1843. $content = ob_get_clean();
  1844. echo apply_filters( 'fl_builder_render_module_html_content', $content, $type, $settings, $module );
  1845. do_action( 'fl_builder_render_module_html_after', $type, $settings, $module );
  1846. }
  1847. /**
  1848. * Renders the HTML attributes for a single module.
  1849. *
  1850. * @since 1.0
  1851. * @param object $module A module node object.
  1852. * @return void
  1853. */
  1854. static public function render_module_attributes( $module ) {
  1855. /**
  1856. * Use this filter to work with the custom class a user adds to a module in the Class field on the Advanced tab.
  1857. * @see fl_builder_module_custom_class
  1858. * @link https://kb.wpbeaverbuilder.com/article/117-plugin-filter-reference
  1859. */
  1860. $custom_class = apply_filters( 'fl_builder_module_custom_class', $module->settings->class, $module );
  1861. $active = FLBuilderModel::is_builder_active();
  1862. $visible = FLBuilderModel::is_node_visible( $module );
  1863. $has_rules = FLBuilderModel::node_has_visibility_rules( $module );
  1864. $attrs = array(
  1865. 'id' => esc_attr( $module->settings->id ),
  1866. 'class' => array(
  1867. 'fl-module',
  1868. 'fl-module-' . $module->settings->type,
  1869. 'fl-node-' . $module->node,
  1870. ),
  1871. 'data-node' => $module->node,
  1872. );
  1873. // Classes
  1874. if ( ! empty( $module->settings->responsive_display ) ) {
  1875. $attrs['class'][] = 'fl-visible-' . $module->settings->responsive_display;
  1876. }
  1877. if ( ! empty( $module->settings->animation ) && is_string( $module->settings->animation ) ) {
  1878. $attrs['class'][] = 'fl-animation fl-' . $module->settings->animation;
  1879. $attrs['data-animation-delay'][] = $module->settings->animation_delay;
  1880. }
  1881. if ( ! empty( $custom_class ) ) {
  1882. $attrs['class'][] = trim( esc_attr( $custom_class ) );
  1883. }
  1884. if ( $active && ! $visible ) {
  1885. $attrs['class'][] = 'fl-node-hidden';
  1886. }
  1887. if ( $active && $has_rules ) {
  1888. $attrs['class'][] = 'fl-node-has-rules';
  1889. }
  1890. // Data
  1891. if ( $active ) {
  1892. $attrs['data-parent'] = $module->parent;
  1893. $attrs['data-type'] = $module->settings->type;
  1894. $attrs['data-name'] = $module->name;
  1895. }
  1896. // Render the attrs
  1897. self::render_node_attributes( apply_filters( 'fl_builder_module_attributes', $attrs, $module ) );
  1898. }
  1899. /**
  1900. * Renders the CSS for a single module.
  1901. *
  1902. * NOTE: This is not used to render CSS for modules in the FLBuilder::render_css
  1903. * method. Instead it is used to render CSS for one module inside of another.
  1904. * For example, you can use this along with FLBuilder::render_module_html to
  1905. * render a button module inside of a callout module. If you need to filter the
  1906. * CSS for the layout, consider using the fl_builder_render_css filter instead.
  1907. *
  1908. * @since 1.0
  1909. * @param string $type The type of module.
  1910. * @param object $id A module node ID.
  1911. * @param object $settings A module settings object.
  1912. * @return void
  1913. */
  1914. static public function render_module_css( $type, $id, $settings ) {
  1915. // Settings
  1916. $global_settings = FLBuilderModel::get_global_settings();
  1917. $defaults = FLBuilderModel::get_module_defaults( $type );
  1918. $settings = (object) array_merge( (array) $defaults, (array) $settings );
  1919. $settings = apply_filters( 'fl_builder_render_module_css_settings', $settings, $id, $type );
  1920. // Module
  1921. $class = get_class( FLBuilderModel::$modules[ $type ] );
  1922. $module = new $class();
  1923. $module->settings = $settings;
  1924. // CSS
  1925. ob_start();
  1926. include $module->dir . 'includes/frontend.css.php';
  1927. $css = ob_get_clean();
  1928. echo apply_filters( 'fl_builder_render_module_css', $css, $module, $id );
  1929. }
  1930. /**
  1931. * Renders the CSS and JS assets.
  1932. *
  1933. * @since 1.7
  1934. * @return void
  1935. */
  1936. static public function render_assets() {
  1937. self::render_css();
  1938. self::render_js();
  1939. }
  1940. /**
  1941. * Renders custom CSS in a style tag so it can be edited
  1942. * using the builder interface.
  1943. *
  1944. * @since 1.7
  1945. * @return void
  1946. */
  1947. static public function render_custom_css_for_editing() {
  1948. if ( ! FLBuilderModel::is_builder_active() ) {
  1949. return;
  1950. }
  1951. $global_settings = FLBuilderModel::get_global_settings();
  1952. $layout_settings = FLBuilderModel::get_layout_settings();
  1953. echo '<style id="fl-builder-global-css">' . $global_settings->css . '</style>';
  1954. echo '<style id="fl-builder-layout-css">' . $layout_settings->css . '</style>';
  1955. }
  1956. /**
  1957. * Renders and caches the CSS for a builder layout.
  1958. *
  1959. * @since 1.0
  1960. * @param bool $include_global
  1961. * @param bool $save
  1962. * @return string
  1963. */
  1964. static public function render_css( $include_global = true, $save = true ) {
  1965. // Get info on the new file.
  1966. $nodes = FLBuilderModel::get_categorized_nodes();
  1967. $node_status = FLBuilderModel::get_node_status();
  1968. $global_settings = FLBuilderModel::get_global_settings();
  1969. $asset_info = FLBuilderModel::get_asset_info();
  1970. $post_id = FLBuilderModel::get_post_id();
  1971. $post = get_post( $post_id );
  1972. $css = '';
  1973. $path = $include_global ? $asset_info['css'] : $asset_info['css_partial'];
  1974. // Render the global css.
  1975. if ( $include_global ) {
  1976. $css .= self::render_global_css();
  1977. }
  1978. // Loop through rows
  1979. foreach ( $nodes['rows'] as $row ) {
  1980. // Instance row css
  1981. ob_start();
  1982. include FL_BUILDER_DIR . 'includes/row-css.php';
  1983. $css .= ob_get_clean();
  1984. // Instance row margins
  1985. $css .= self::render_row_margins( $row );
  1986. // Instance row padding
  1987. $css .= self::render_row_padding( $row );
  1988. // Instance row border
  1989. $css .= self::render_row_border( $row );
  1990. }
  1991. // Loop through the columns.
  1992. foreach ( $nodes['columns'] as $col ) {
  1993. // Instance column css
  1994. ob_start();
  1995. include FL_BUILDER_DIR . 'includes/column-css.php';
  1996. $css .= ob_get_clean();
  1997. // Instance column margins
  1998. $css .= self::render_column_margins( $col );
  1999. // Instance column padding
  2000. $css .= self::render_column_padding( $col );
  2001. // Instance column border
  2002. $css .= self::render_column_border( $col );
  2003. // Get the modules in this column.
  2004. $modules = FLBuilderModel::get_modules( $col );
  2005. }
  2006. // Loop through the modules.
  2007. foreach ( $nodes['modules'] as $module ) {
  2008. // Global module css
  2009. $file = $module->dir . 'css/frontend.css';
  2010. $file_responsive = $module->dir . 'css/frontend.responsive.css';
  2011. // Only include global module css that hasn't been included yet.
  2012. if ( ! in_array( $module->settings->type . '-module-css', self::$enqueued_global_assets ) ) {
  2013. // Add to the compiled array so we don't include it again.
  2014. self::$enqueued_global_assets[] = $module->settings->type . '-module-css';
  2015. // Get the standard module css.
  2016. if ( fl_builder_filesystem()->file_exists( $file ) ) {
  2017. $css .= fl_builder_filesystem()->file_get_contents( $file );
  2018. }
  2019. // Get the responsive module css.
  2020. if ( $global_settings->responsive_enabled && fl_builder_filesystem()->file_exists( $file_responsive ) ) {
  2021. $css .= '@media (max-width: ' . $global_settings->responsive_breakpoint . 'px) { ';
  2022. $css .= fl_builder_filesystem()->file_get_contents( $file_responsive );
  2023. $css .= ' }';
  2024. }
  2025. }
  2026. // Instance module css
  2027. $file = $module->dir . 'includes/frontend.css.php';
  2028. $settings = $module->settings;
  2029. $id = $module->node;
  2030. if ( fl_builder_filesystem()->file_exists( $file ) ) {
  2031. ob_start();
  2032. include $file;
  2033. $css .= ob_get_clean();
  2034. }
  2035. // Instance module margins
  2036. $css .= self::render_module_margins( $module );
  2037. if ( ! isset( $global_settings->auto_spacing ) || $global_settings->auto_spacing ) {
  2038. $css .= self::render_responsive_module_margins( $module );
  2039. }
  2040. }
  2041. // Custom Global CSS (included here for proper specificity)
  2042. if ( 'published' == $node_status && $include_global ) {
  2043. $css .= $global_settings->css;
  2044. }
  2045. // Custom Global Nodes CSS
  2046. $css .= self::render_global_nodes_custom_code( 'css' );
  2047. // Custom Layout CSS
  2048. if ( 'published' == $node_status ) {
  2049. $css .= FLBuilderModel::get_layout_settings()->css;
  2050. }
  2051. /**
  2052. * Use this filter to modify the CSS that is compiled and cached for each builder layout.
  2053. * @see fl_builder_render_css
  2054. * @link https://kb.wpbeaverbuilder.com/article/117-plugin-filter-reference
  2055. */
  2056. $css = apply_filters( 'fl_builder_render_css', $css, $nodes, $global_settings, $include_global );
  2057. // Minify the CSS.
  2058. if ( ! self::is_debug() ) {
  2059. $css = preg_replace( '!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css );
  2060. $css = str_replace( array( "\r\n", "\r", "\n", "\t", ' ', ' ', ' ' ), '', $css );
  2061. }
  2062. // Save the CSS.
  2063. if ( $save ) {
  2064. fl_builder_filesystem()->file_put_contents( $path, $css );
  2065. }
  2066. do_action( 'fl_builder_after_render_css' );
  2067. return $css;
  2068. }
  2069. /**
  2070. * Renders the CSS used for all builder layouts.
  2071. *
  2072. * @since 1.8.2
  2073. * @return string
  2074. */
  2075. static public function render_global_css() {
  2076. // Get info on the new file.
  2077. $global_settings = FLBuilderModel::get_global_settings();
  2078. // Core layout css
  2079. $css = fl_builder_filesystem()->file_get_contents( FL_BUILDER_DIR . '/css/fl-builder-layout.css' );
  2080. // Core button defaults
  2081. if ( ! defined( 'FL_THEME_VERSION' ) ) {
  2082. $css .= fl_builder_filesystem()->file_get_contents( FL_BUILDER_DIR . '/css/fl-builder-layout-button-defaults.css' );
  2083. }
  2084. // Core layout RTL css
  2085. if ( is_rtl() ) {
  2086. $css .= fl_builder_filesystem()->file_get_contents( FL_BUILDER_DIR . '/css/fl-builder-layout-rtl.css' );
  2087. }
  2088. // Global node css
  2089. foreach ( array(
  2090. array( 'row_margins', '.fl-row-content-wrap { margin: ' ),
  2091. array( 'row_padding', '.fl-row-content-wrap { padding: ' ),
  2092. array( 'row_width', '.fl-row-fixed-width { max-width: ' ),
  2093. array( 'module_margins', '.fl-module-content { margin: ' ),
  2094. ) as $data ) {
  2095. if ( '' !== $global_settings->{ $data[0] } ) {
  2096. $value = preg_replace( self::regex( 'css_unit' ), '', strtolower( $global_settings->{ $data[0] } ) );
  2097. $css .= $data[1] . esc_attr( $value );
  2098. $css .= ( is_numeric( $value ) ) ? ( 'px; }' ) : ( '; }' );
  2099. }
  2100. }
  2101. // Responsive layout css
  2102. if ( $global_settings->responsive_enabled ) {
  2103. // Medium devices
  2104. $css .= '@media (max-width: ' . $global_settings->medium_breakpoint . 'px) { ';
  2105. // Core medium layout css
  2106. $css .= fl_builder_filesystem()->file_get_contents( FL_BUILDER_DIR . '/css/fl-builder-layout-medium.css' );
  2107. // Global node medium css
  2108. foreach ( array(
  2109. array( 'row_margins_medium', '.fl-row[data-node] > .fl-row-content-wrap { margin: ' ),
  2110. array( 'row_padding_medium', '.fl-row[data-node] > .fl-row-content-wrap { padding: ' ),
  2111. array( 'module_margins_medium', '.fl-module[data-node] > .fl-module-content { margin: ' ),
  2112. ) as $data ) {
  2113. if ( '' !== $global_settings->{ $data[0] } ) {
  2114. $value = preg_replace( self::regex( 'css_unit' ), '', strtolower( $global_settings->{ $data[0] } ) );
  2115. $css .= $data[1] . esc_attr( $value );
  2116. $css .= ( is_numeric( $value ) ) ? ( 'px; }' ) : ( '; }' );
  2117. }
  2118. }
  2119. $css .= ' }';
  2120. // Responsive devices
  2121. $css .= '@media (max-width: ' . $global_settings->responsive_breakpoint . 'px) { ';
  2122. // Core responsive layout css
  2123. $css .= fl_builder_filesystem()->file_get_contents( FL_BUILDER_DIR . '/css/fl-builder-layout-responsive.css' );
  2124. // Auto spacing
  2125. if ( ! isset( $global_settings->auto_spacing ) || $global_settings->auto_spacing ) {
  2126. $css .= fl_builder_filesystem()->file_get_contents( FL_BUILDER_DIR . '/css/fl-builder-layout-auto-spacing.css' );
  2127. }
  2128. // Global node responsive css
  2129. foreach ( array(
  2130. array( 'row_margins_responsive', '.fl-row[data-node] > .fl-row-content-wrap { margin: ' ),
  2131. array( 'row_padding_responsive', '.fl-row[data-node] > .fl-row-content-wrap { padding: ' ),
  2132. array( 'module_margins_responsive', '.fl-module[data-node] > .fl-module-content { margin: ' ),
  2133. ) as $data ) {
  2134. if ( '' !== $global_settings->{ $data[0] } ) {
  2135. $value = preg_replace( self::regex( 'css_unit' ), '', strtolower( $global_settings->{ $data[0] } ) );
  2136. $css .= $data[1] . esc_attr( $value );
  2137. $css .= ( is_numeric( $value ) ) ? ( 'px; }' ) : ( '; }' );
  2138. }
  2139. }
  2140. $css .= ' }';
  2141. }
  2142. // Default page heading
  2143. if ( FLBuilderModel::is_builder_enabled() ) {
  2144. if ( ! $global_settings->show_default_heading && ! empty( $global_settings->default_heading_selector ) ) {
  2145. $heading_selector = esc_attr( $global_settings->default_heading_selector );
  2146. // If the value starts with `body` or `.fl-builder` selector, we use custom selectors
  2147. if ( 0 === strpos( $heading_selector, 'body' ) || 0 === strpos( $heading_selector, '.fl-builder' ) ) {
  2148. $css .= $heading_selector;
  2149. } else {
  2150. $css .= '.page ' . $heading_selector . ', .single-fl-builder-template ' . $heading_selector;
  2151. }
  2152. $css .= ' { display:none; }';
  2153. }
  2154. }
  2155. return $css;
  2156. }
  2157. /**
  2158. * Forcing HTTPS in URLs when `FLBuilderModel::is_ssl()` returns TRUE
  2159. *
  2160. * @since 1.7.6
  2161. * @param string $content A string where the URLs will be modified.
  2162. * @return string String with SSL ready URLs.
  2163. */
  2164. static public function rewrite_css_cache_urls( $content ) {
  2165. if ( FLBuilderModel::is_ssl() ) {
  2166. $content = str_ireplace( 'http:', 'https:', $content );
  2167. }
  2168. return $content;
  2169. }
  2170. /**
  2171. * Regular expressions.
  2172. *
  2173. * @since 1.9
  2174. * @param string $scope What regular expression to return?
  2175. * @return string Regular expression.
  2176. */
  2177. static public function regex( $scope ) {
  2178. $regex = array(
  2179. 'css_unit' => '/[^a-z0-9%.\-]/',
  2180. );
  2181. return ( isset( $regex[ $scope ] ) ) ? $regex[ $scope ] : null;
  2182. }
  2183. /**
  2184. * Renders the CSS spacing and border properties for a node.
  2185. *
  2186. * @param object $node A generic node object.
  2187. * @param string $prop_type One of [ 'padding', 'margin', 'border' ].
  2188. * @param string $selector_prefix Optional CSS selector prefix for better overrides.
  2189. * @return string A CSS string.
  2190. */
  2191. static public function render_node_spacing( $node = null, $prop_type = '', $selector_prefix = '' ) {
  2192. // Exit early if incorrect parameters
  2193. if ( ! is_object( $node ) || empty( $prop_type ) ) {
  2194. return;
  2195. }
  2196. $prop_type = strtolower( $prop_type );
  2197. // Ensure type is valid
  2198. if ( ! in_array( $prop_type, array( 'margin', 'padding', 'border' ), true ) ) {
  2199. return;
  2200. }
  2201. $global_settings = FLBuilderModel::get_global_settings();
  2202. $settings = $node->settings;
  2203. $css = '';
  2204. $selector_prefix .= ' .fl-node-' . $node->node;
  2205. // Determine selector suffix to apply spacing to
  2206. switch ( $node->type ) {
  2207. case 'row':
  2208. $selector_suffix = ' > .fl-row-content-wrap';
  2209. break;
  2210. case 'column':
  2211. $selector_suffix = ' > .fl-col-content';
  2212. break;
  2213. case 'module':
  2214. $selector_suffix = ' > .fl-module-content';
  2215. break;
  2216. }
  2217. // Create rules for each breakpoint
  2218. foreach ( array( 'default', 'medium', 'responsive' ) as $breakpoint ) {
  2219. $breakpoint_css = '';
  2220. $setting_suffix = ( 'default' !== $breakpoint ) ? '_' . $breakpoint : '';
  2221. // Iterate over each direction
  2222. foreach ( array( 'top', 'right', 'bottom', 'left' ) as $dir ) {
  2223. $setting = $prop_type . '_' . $dir . $setting_suffix;
  2224. if ( ! isset( $settings->{ $setting } ) ) {
  2225. continue;
  2226. }
  2227. $prop = $prop_type . '-' . $dir;
  2228. $value = preg_replace( self::regex( 'css_unit' ), '', strtolower( $settings->{ $setting } ) );
  2229. if ( 'border' === $prop_type ) {
  2230. if ( empty( $settings->border_type ) ) {
  2231. continue;
  2232. } else {
  2233. $prop .= '-width';
  2234. }
  2235. }
  2236. if ( '' !== $value ) {
  2237. $breakpoint_css .= "\t";
  2238. $breakpoint_css .= $prop . ':' . esc_attr( $value );
  2239. $breakpoint_css .= ( is_numeric( trim( $value ) ) ) ? ( 'px;' ) : ( ';' );
  2240. $breakpoint_css .= "\r\n";
  2241. }
  2242. }
  2243. if ( ! empty( $breakpoint_css ) ) {
  2244. // Build the selector
  2245. if ( 'default' !== $breakpoint ) {
  2246. $selector = $selector_prefix . '.fl-' . str_replace( 'column', 'col', $node->type ) . $selector_suffix;
  2247. } else {
  2248. $selector = $selector_prefix . $selector_suffix;
  2249. }
  2250. // Wrap css in selector
  2251. $breakpoint_css = $selector . ' {' . "\r\n" . $breakpoint_css . '}' . "\r\n";
  2252. // Wrap css in media query
  2253. if ( 'default' !== $breakpoint ) {
  2254. $breakpoint_css = '@media ( max-width: ' . $global_settings->{ $breakpoint . '_breakpoint' } . 'px ) {' . "\r\n" . $breakpoint_css . '}' . "\r\n";
  2255. }
  2256. $css .= $breakpoint_css;
  2257. }
  2258. }
  2259. return $css;
  2260. }
  2261. /**
  2262. * Renders the CSS margins for a row.
  2263. *
  2264. * @since 1.0
  2265. * @param object $row A row node object.
  2266. * @return string The row CSS margins string.
  2267. */
  2268. static public function render_row_margins( $row ) {
  2269. return self::render_node_spacing( $row, 'margin' );
  2270. }
  2271. /**
  2272. * Renders the CSS padding for a row.
  2273. *
  2274. * @since 1.0
  2275. * @param object $row A row node object.
  2276. * @return string The row CSS padding string.
  2277. */
  2278. static public function render_row_padding( $row ) {
  2279. return self::render_node_spacing( $row, 'padding' );
  2280. }
  2281. /**
  2282. * Renders the CSS border widths for a row.
  2283. *
  2284. * @since 1.9
  2285. * @param object $row A row node object.
  2286. * @return string The row CSS border-width string.
  2287. */
  2288. static public function render_row_border( $row ) {
  2289. return self::render_node_spacing( $row, 'border' );
  2290. }
  2291. /**
  2292. * Renders the CSS margins for a column.
  2293. *
  2294. * @since 1.0
  2295. * @param object $col A column node object.
  2296. * @return string The column CSS margins string.
  2297. */
  2298. static public function render_column_margins( $col ) {
  2299. return self::render_node_spacing( $col, 'margin' );
  2300. }
  2301. /**
  2302. * Renders the CSS padding for a column.
  2303. *
  2304. * @since 1.0
  2305. * @param object $col A column node object.
  2306. * @return string The column CSS padding string.
  2307. */
  2308. static public function render_column_padding( $col ) {
  2309. return self::render_node_spacing( $col, 'padding' );
  2310. }
  2311. /**
  2312. * Renders the CSS border widths for a column.
  2313. *
  2314. * @since 1.9
  2315. * @param object $col A column node object.
  2316. * @return string The column CSS border-width string.
  2317. */
  2318. static public function render_column_border( $col ) {
  2319. return self::render_node_spacing( $col, 'border', '.fl-builder-content' );
  2320. }
  2321. /**
  2322. * Renders the CSS margins for a module.
  2323. *
  2324. * @since 1.0
  2325. * @param object $module A module node object.
  2326. * @return string The module CSS margins string.
  2327. */
  2328. static public function render_module_margins( $module ) {
  2329. return self::render_node_spacing( $module, 'margin' );
  2330. }
  2331. /**
  2332. * Renders the (auto) responsive CSS margins for a module.
  2333. *
  2334. * @since 1.0
  2335. * @param object $module A module node object.
  2336. * @return string The module CSS margins string.
  2337. */
  2338. static public function render_responsive_module_margins( $module ) {
  2339. $global_settings = FLBuilderModel::get_global_settings();
  2340. $settings = $module->settings;
  2341. $margins = '';
  2342. $css = '';
  2343. // Bail early if we have global responsive margins.
  2344. if ( '' != $global_settings->module_margins_responsive ) {
  2345. return $css;
  2346. }
  2347. // Get the global default margin value to use.
  2348. if ( '' != $global_settings->module_margins_medium ) {
  2349. $default = trim( $global_settings->module_margins_medium );
  2350. } else {
  2351. $default = trim( $global_settings->module_margins );
  2352. }
  2353. // Set the responsive margin CSS if necessary.
  2354. foreach ( array( 'top', 'bottom', 'left', 'right' ) as $dimension ) {
  2355. $responsive = 'margin_' . $dimension . '_responsive';
  2356. $medium = 'margin_' . $dimension . '_responsive';
  2357. $desktop = 'margin_' . $dimension;
  2358. if ( '' == $settings->$responsive ) {
  2359. $value = '' == $settings->$medium ? $settings->$desktop : $settings->$medium;
  2360. if ( '' != $value && ( $value > $default || $value < 0 ) ) {
  2361. $margins .= 'margin-' . $dimension . ':' . esc_attr( $default ) . 'px;';
  2362. }
  2363. }
  2364. }
  2365. // Set the media query if we have margins.
  2366. if ( '' !== $margins ) {
  2367. $css .= '@media (max-width: ' . esc_attr( $global_settings->responsive_breakpoint ) . 'px) { ';
  2368. $css .= '.fl-node-' . $module->node . ' > .fl-module-content { ' . $margins . ' }';
  2369. $css .= ' }';
  2370. }
  2371. return $css;
  2372. }
  2373. /**
  2374. * Renders and caches the JavaScript for a builder layout.
  2375. *
  2376. * @since 1.0
  2377. * @param bool $include_global
  2378. * @param bool $save
  2379. * @return string
  2380. */
  2381. static public function render_js( $include_global = true, $save = true ) {
  2382. // Get info on the new file.
  2383. $nodes = FLBuilderModel::get_categorized_nodes();
  2384. $global_settings = FLBuilderModel::get_global_settings();
  2385. $layout_settings = FLBuilderModel::get_layout_settings();
  2386. $rows = FLBuilderModel::get_nodes( 'row' );
  2387. $asset_info = FLBuilderModel::get_asset_info();
  2388. $js = '';
  2389. $path = $include_global ? $asset_info['js'] : $asset_info['js_partial'];
  2390. // Render the global js.
  2391. if ( $include_global && ! isset( $_GET['safemode'] ) ) {
  2392. $js .= self::render_global_js();
  2393. }
  2394. // Loop through the rows.
  2395. foreach ( $nodes['rows'] as $row ) {
  2396. $js .= self::render_row_js( $row );
  2397. }
  2398. // Loop through the modules.
  2399. foreach ( $nodes['modules'] as $module ) {
  2400. $js .= self::render_module_js( $module );
  2401. }
  2402. // Add the layout settings JS.
  2403. if ( ! isset( $_GET['safemode'] ) ) {
  2404. $js .= self::render_global_nodes_custom_code( 'js' );
  2405. $js .= ( is_array( $layout_settings->js ) || is_object( $layout_settings->js ) ) ? json_encode( $layout_settings->js ) : $layout_settings->js;
  2406. }
  2407. // Call the FLBuilder._renderLayoutComplete method if we're currently editing.
  2408. if ( stristr( $asset_info['js'], '-draft.js' ) || stristr( $asset_info['js'], '-preview.js' ) ) {
  2409. $js .= "; if(typeof FLBuilder !== 'undefined' && typeof FLBuilder._renderLayoutComplete !== 'undefined') FLBuilder._renderLayoutComplete();";
  2410. }
  2411. // Include FLJSMin
  2412. if ( ! class_exists( 'FLJSMin' ) ) {
  2413. include FL_BUILDER_DIR . 'classes/class-fl-jsmin.php';
  2414. }
  2415. /**
  2416. * Use this filter to modify the JavaScript that is compiled and cached for each builder layout.
  2417. * @see fl_builder_render_js
  2418. * @link https://kb.wpbeaverbuilder.com/article/117-plugin-filter-reference
  2419. */
  2420. $js = apply_filters( 'fl_builder_render_js', $js, $nodes, $global_settings, $include_global );
  2421. // Only proceed if we have JS.
  2422. if ( ! empty( $js ) ) {
  2423. // Minify the JS.
  2424. if ( ! self::is_debug() ) {
  2425. try {
  2426. $min = FLJSMin::minify( $js );
  2427. } catch ( Exception $e ) {}
  2428. if ( isset( $min ) ) {
  2429. $js = $min;
  2430. }
  2431. }
  2432. // Save the JS.
  2433. if ( $save ) {
  2434. fl_builder_filesystem()->file_put_contents( $path, $js );
  2435. }
  2436. do_action( 'fl_builder_after_render_js' );
  2437. }
  2438. return $js;
  2439. }
  2440. /**
  2441. * Renders the JS used for all builder layouts.
  2442. *
  2443. * @since 1.8.2
  2444. * @return string
  2445. */
  2446. static public function render_global_js() {
  2447. $global_settings = FLBuilderModel::get_global_settings();
  2448. $js = '';
  2449. // Add the path legacy vars (FLBuilderLayoutConfig.paths should be used instead).
  2450. $js .= "var wpAjaxUrl = '" . admin_url( 'admin-ajax.php' ) . "';";
  2451. $js .= "var flBuilderUrl = '" . FL_BUILDER_URL . "';";
  2452. // Layout config object.
  2453. ob_start();
  2454. include FL_BUILDER_DIR . 'includes/layout-js-config.php';
  2455. $js .= ob_get_clean();
  2456. // Core layout JS.
  2457. $js .= fl_builder_filesystem()->file_get_contents( FL_BUILDER_DIR . 'js/fl-builder-layout.js' );
  2458. // Add the global settings JS.
  2459. $js .= $global_settings->js;
  2460. return $js;
  2461. }
  2462. /**
  2463. * Renders the JavaScript for a single row.
  2464. *
  2465. * @since 1.7
  2466. * @param string|object $row_id A row ID or object.
  2467. * @return string
  2468. */
  2469. static public function render_row_js( $row_id ) {
  2470. $row = is_object( $row_id ) ? $row_id : FLBuilderModel::get_node( $row_id );
  2471. $settings = $row->settings;
  2472. $id = $row->node;
  2473. ob_start();
  2474. include FL_BUILDER_DIR . 'includes/row-js.php';
  2475. return ob_get_clean();
  2476. }
  2477. /**
  2478. * Renders the JavaScript for all modules in a single row.
  2479. *
  2480. * @since 1.7
  2481. * @param string|object $row_id A row ID or object.
  2482. * @return string
  2483. */
  2484. static public function render_row_modules_js( $row_id ) {
  2485. $row = is_object( $row_id ) ? $row_id : FLBuilderModel::get_node( $row_id );
  2486. $nodes = FLBuilderModel::get_categorized_nodes();
  2487. $template_post_id = FLBuilderModel::is_node_global( $row );
  2488. $js = '';
  2489. // Render the JS.
  2490. foreach ( $nodes['groups'] as $group ) {
  2491. if ( $row->node == $group->parent || ( $template_post_id && $row->template_node_id == $group->parent ) ) {
  2492. foreach ( $nodes['columns'] as $column ) {
  2493. if ( $group->node == $column->parent ) {
  2494. foreach ( $nodes['modules'] as $module ) {
  2495. if ( $column->node == $module->parent ) {
  2496. $js .= self::render_module_js( $module );
  2497. }
  2498. }
  2499. }
  2500. }
  2501. }
  2502. }
  2503. // Return the JS.
  2504. return $js;
  2505. }
  2506. /**
  2507. * Renders the JavaScript for all modules in a single column.
  2508. *
  2509. * @since 1.7
  2510. * @param string|object $col_id A column ID or object.
  2511. * @return string
  2512. */
  2513. static public function render_column_modules_js( $col_id ) {
  2514. $col = is_object( $col_id ) ? $col_id : FLBuilderModel::get_node( $col_id );
  2515. $nodes = FLBuilderModel::get_categorized_nodes();
  2516. $js = '';
  2517. // Render the JS.
  2518. foreach ( $nodes['modules'] as $module ) {
  2519. if ( $col->node == $module->parent ) {
  2520. $js .= self::render_module_js( $module );
  2521. }
  2522. }
  2523. // Return the JS.
  2524. return $js;
  2525. }
  2526. /**
  2527. * Renders the JavaScript for a single module.
  2528. *
  2529. * @since 1.7
  2530. * @param string|object $module_id A module ID or object.
  2531. * @return string
  2532. */
  2533. static public function render_module_js( $module_id ) {
  2534. $module = is_object( $module_id ) ? $module_id : FLBuilderModel::get_module( $module_id );
  2535. $global_settings = FLBuilderModel::get_global_settings();
  2536. $js = '';
  2537. // Global module JS
  2538. $file = $module->dir . 'js/frontend.js';
  2539. if ( fl_builder_filesystem()->file_exists( $file ) && ! in_array( $module->settings->type . '-module-js', self::$enqueued_global_assets ) ) {
  2540. $js .= "\n" . fl_builder_filesystem()->file_get_contents( $file );
  2541. self::$enqueued_global_assets[] = $module->settings->type . '-module-js';
  2542. }
  2543. // Instance module JS
  2544. $file = $module->dir . 'includes/frontend.js.php';
  2545. $settings = $module->settings;
  2546. $id = $module->node;
  2547. if ( fl_builder_filesystem()->file_exists( $file ) ) {
  2548. ob_start();
  2549. include $file;
  2550. $js .= ob_get_clean();
  2551. }
  2552. // Return the JS.
  2553. return $js;
  2554. }
  2555. /**
  2556. * Renders the custom CSS or JS for all global nodes in a layout.
  2557. *
  2558. * @since 1.7
  2559. */
  2560. static public function render_global_nodes_custom_code( $type = 'css' ) {
  2561. $code = '';
  2562. $rendered = array();
  2563. if ( ! FLBuilderModel::is_post_node_template() ) {
  2564. $nodes = FLBuilderModel::get_layout_data();
  2565. $node_status = FLBuilderModel::get_node_status();
  2566. foreach ( $nodes as $node_id => $node ) {
  2567. $template_post_id = FLBuilderModel::is_node_global( $node );
  2568. if ( $template_post_id && ! in_array( $template_post_id, $rendered ) ) {
  2569. $rendered[] = $template_post_id;
  2570. $code .= FLBuilderModel::get_layout_settings( $node_status, $template_post_id )->{ $type };
  2571. }
  2572. }
  2573. }
  2574. return $code;
  2575. }
  2576. /**
  2577. * Check if publish should require page to refresh.
  2578. *
  2579. * @since 2.0
  2580. * @return void
  2581. */
  2582. static public function should_refresh_on_publish() {
  2583. $refresh = ! is_admin_bar_showing();
  2584. return apply_filters( 'fl_builder_should_refresh_on_publish', $refresh );
  2585. }
  2586. /**
  2587. * Custom logging function that handles objects and arrays.
  2588. *
  2589. * @since 1.0
  2590. * @return void
  2591. */
  2592. static public function log() {
  2593. foreach ( func_get_args() as $arg ) {
  2594. ob_start();
  2595. print_r( $arg );
  2596. error_log( ob_get_clean() );
  2597. }
  2598. }
  2599. /**
  2600. * Filter WP uploads and check filetype is valid for photo and video modules.
  2601. * @since 1.10.8
  2602. */
  2603. static public function wp_handle_upload_prefilter_filter( $file ) {
  2604. $type = isset( $_POST['fl_upload_type'] ) ? $_POST['fl_upload_type'] : false;
  2605. $ext = pathinfo( $file['name'], PATHINFO_EXTENSION );
  2606. $regex = array(
  2607. 'photo' => '#(jpe?g|png|gif|bmp|tiff?)#i',
  2608. 'video' => '#(mp4|m4v|webm)#i',
  2609. );
  2610. if ( ! $type ) {
  2611. return $file;
  2612. }
  2613. $regex = apply_filters( 'fl_module_upload_regex', $regex, $type, $ext, $file );
  2614. if ( ! preg_match( $regex[ $type ], $ext ) ) {
  2615. $file['error'] = sprintf( __( 'The uploaded file is not a valid %s extension.', 'fl-builder' ), $type );
  2616. }
  2617. return $file;
  2618. }
  2619. /**
  2620. * Default HTML for no image.
  2621. * @since 1.10.8
  2622. * @return string
  2623. */
  2624. static public function default_image_html( $classes ) {
  2625. return sprintf( '<img src="%s" class="%s" />', FL_BUILDER_URL . 'img/no-image.png', $classes );
  2626. }
  2627. /**
  2628. * Check if debug is enabled.
  2629. * @since 1.10.8.2
  2630. * @return bool
  2631. */
  2632. static public function is_debug() {
  2633. $debug = false;
  2634. if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
  2635. $debug = true;
  2636. }
  2637. return apply_filters( 'fl_is_debug', $debug );
  2638. }
  2639. /**
  2640. * Get the fa5 url.
  2641. * @since 2.2
  2642. * @return string url
  2643. */
  2644. static public function get_fa5_url() {
  2645. return ( apply_filters( 'fl_enable_fa5_pro', false ) ) ? self::$fa5_pro_url : plugins_url( '/fonts/fontawesome/css/all.min.css', FL_BUILDER_FILE );
  2646. }
  2647. /**
  2648. * @since 1.0
  2649. * @deprecated 1.7.4
  2650. */
  2651. static public function layout_styles_scripts( $post_id ) {
  2652. _deprecated_function( __METHOD__, '1.7.4', __CLASS__ . '::enqueue_layout_styles_scripts()' );
  2653. self::enqueue_layout_styles_scripts();
  2654. }
  2655. /**
  2656. * @since 1.0
  2657. * @deprecated 1.7.4
  2658. */
  2659. static public function styles_scripts() {
  2660. _deprecated_function( __METHOD__, '1.7.4', __CLASS__ . '::enqueue_ui_styles_scripts()' );
  2661. self::enqueue_ui_styles_scripts();
  2662. }
  2663. /**
  2664. * @since 1.0
  2665. * @deprecated 1.8
  2666. */
  2667. static public function register_templates_post_type() {
  2668. _deprecated_function( __METHOD__, '1.8', 'FLBuilderUserTemplates::register_post_type()' );
  2669. if ( class_exists( 'FLBuilderUserTemplates' ) ) {
  2670. FLBuilderUserTemplates::register_post_type();
  2671. }
  2672. }
  2673. /**
  2674. * @since 1.0
  2675. * @deprecated 1.8
  2676. */
  2677. static public function render_template( $template ) {
  2678. _deprecated_function( __METHOD__, '1.8', 'FLBuilderUserTemplates::template_include()' );
  2679. if ( class_exists( 'FLBuilderUserTemplates' ) ) {
  2680. FLBuilderUserTemplates::template_include();
  2681. }
  2682. }
  2683. /**
  2684. * @since 1.6.3
  2685. * @deprecated 1.8
  2686. */
  2687. static public function render_ui_panel_node_templates() {
  2688. _deprecated_function( __METHOD__, '1.8', 'FLBuilderUserTemplates::render_ui_panel_node_templates()' );
  2689. if ( class_exists( 'FLBuilderUserTemplates' ) ) {
  2690. FLBuilderUserTemplates::render_ui_panel_node_templates();
  2691. }
  2692. }
  2693. /**
  2694. * @since 1.0
  2695. * @deprecated 1.8
  2696. */
  2697. static public function render_user_template_settings() {
  2698. _deprecated_function( __METHOD__, '1.8', 'FLBuilderUserTemplates::render_settings()' );
  2699. if ( class_exists( 'FLBuilderUserTemplates' ) ) {
  2700. FLBuilderUserTemplates::render_settings();
  2701. }
  2702. }
  2703. /**
  2704. * @since 1.6.3
  2705. * @deprecated 1.8
  2706. */
  2707. static public function render_node_template_settings( $node_id = null ) {
  2708. _deprecated_function( __METHOD__, '1.8', 'FLBuilderUserTemplates::render_node_settings()' );
  2709. if ( class_exists( 'FLBuilderUserTemplates' ) ) {
  2710. FLBuilderUserTemplates::render_node_settings( $node_id );
  2711. }
  2712. }
  2713. /**
  2714. * @since 1.0
  2715. * @deprecated 2.0
  2716. */
  2717. static public function render_template_selector() {
  2718. _deprecated_function( __METHOD__, '2.0' );
  2719. return array(
  2720. 'html' => '',
  2721. );
  2722. }
  2723. /**
  2724. * @since 1.8
  2725. * @deprecated 2.0
  2726. */
  2727. static public function render_ui_panel_row_templates() {
  2728. _deprecated_function( __METHOD__, '2.0' );
  2729. }
  2730. /**
  2731. * @since 1.8
  2732. * @deprecated 2.0
  2733. */
  2734. static public function render_ui_panel_modules_templates() {
  2735. _deprecated_function( __METHOD__, '2.0' );
  2736. }
  2737. /**
  2738. * @since 1.8
  2739. * @deprecated 2.0
  2740. */
  2741. static public function render_layout_settings() {
  2742. _deprecated_function( __METHOD__, '2.0' );
  2743. }
  2744. /**
  2745. * @since 1.0
  2746. * @deprecated 2.0
  2747. */
  2748. static public function render_global_settings() {
  2749. _deprecated_function( __METHOD__, '2.0' );
  2750. }
  2751. /**
  2752. * @since 1.0
  2753. * @deprecated 2.0
  2754. */
  2755. static public function render_row_settings( $node_id = null ) {
  2756. _deprecated_function( __METHOD__, '2.0' );
  2757. }
  2758. /**
  2759. * @since 1.0
  2760. * @deprecated 2.0
  2761. */
  2762. static public function render_column_settings( $node_id = null ) {
  2763. _deprecated_function( __METHOD__, '2.0' );
  2764. }
  2765. /**
  2766. * @since 1.0
  2767. * @deprecated 2.0
  2768. */
  2769. static public function render_module_settings( $node_id = null, $type = null, $parent_id = null, $render_state = true ) {
  2770. _deprecated_function( __METHOD__, '2.0' );
  2771. }
  2772. /**
  2773. * @since 2.0.1
  2774. * @deprecated 2.0.7
  2775. */
  2776. static public function render_settings_config() {
  2777. _deprecated_function( __METHOD__, '2.0.7', 'FLBuilderUISettingsForms::render_settings_config()' );
  2778. FLBuilderUISettingsForms::render_settings_config();
  2779. }
  2780. }
  2781. FLBuilder::init();