modified file `w3-total-cache`

This commit is contained in:
KawaiiPunk 2024-03-28 09:40:10 +00:00 committed by Gitium
parent 218272bbcb
commit 34dbbce9b4
3707 changed files with 392891 additions and 166170 deletions

View File

@ -3,7 +3,7 @@
* Plugin Name: ActivityPub
* Plugin URI: https://github.com/pfefferle/wordpress-activitypub/
* Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format.
* Version: 1.3.0
* Version: 2.0.1
* Author: Matthias Pfefferle & Automattic
* Author URI: https://automattic.com/
* License: MIT
@ -29,10 +29,11 @@ require_once __DIR__ . '/includes/functions.php';
\defined( 'ACTIVITYPUB_SHOW_PLUGIN_RECOMMENDATIONS' ) || \define( 'ACTIVITYPUB_SHOW_PLUGIN_RECOMMENDATIONS', true );
\defined( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS' ) || \define( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS', 3 );
\defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=<p>)|(?<=<br>)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' );
\defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9_-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' );
\defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9\._-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' );
\defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "<strong>[ap_title]</strong>\n\n[ap_content]\n\n[ap_hashtags]\n\n[ap_shortlink]" );
\defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false );
\defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false );
\defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false );
\define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
\define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
@ -171,7 +172,7 @@ function plugin_settings_link( $actions ) {
*/
add_action(
'bp_include',
function() {
function () {
require_once __DIR__ . '/integration/class-buddypress.php';
Integration\Buddypress::init();
},

View File

@ -11,10 +11,11 @@ jQuery( function( $ ) {
$( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false );
}
} );
$(document).on( 'wp-plugin-install-success', function( event, response ) {
setTimeout( function() {
$( '.activate-now' ).removeClass( 'thickbox open-plugin-details-modal' );
}, 1200 );
} );
} );
} );

View File

@ -1 +1 @@
<?php return array('dependencies' => array('wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '6aeec6336fd28aa836a7');
<?php return array( 'dependencies' => array( 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives' ), 'version' => '3ffce3edc6fed284bfbc' );

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<?php return array('dependencies' => array('wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '5b48281e37700a970a66');
<?php return array( 'dependencies' => array( 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives' ), 'version' => '40f3434fe6f953826373' );

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url'), 'version' => '59d9702e06860a6d13e4');
<?php return array( 'dependencies' => array( 'react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url' ), 'version' => 'c338a0364a63e21934ae' );

View File

@ -1,3 +1,3 @@
(()=>{var e={184:(e,t)=>{var a;!function(){"use strict";var n={}.hasOwnProperty;function l(){for(var e=[],t=0;t<arguments.length;t++){var a=arguments[t];if(a){var r=typeof a;if("string"===r||"number"===r)e.push(a);else if(Array.isArray(a)){if(a.length){var o=l.apply(null,a);o&&e.push(o)}}else if("object"===r){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){e.push(a.toString());continue}for(var i in a)n.call(a,i)&&a[i]&&e.push(i)}}}return e.join(" ")}e.exports?(l.default=l,e.exports=l):void 0===(a=function(){return l}.apply(t,[]))||(e.exports=a)}()}},t={};function a(n){var l=t[n];if(void 0!==l)return l.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,a),r.exports}a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";const e=window.wp.blocks,t=window.wp.element,n=window.wp.primitives,l=(0,t.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,t.createElement)(n.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"}));function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var a=arguments[t];for(var n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n])}return e},r.apply(this,arguments)}const o=window.wp.components,i=window.wp.blockEditor,c=window.wp.i18n,s=window.React,p=window.wp.apiFetch;var u=a.n(p);const v=window.wp.url;var m=a(184),b=a.n(m);function w(e){let{active:a,children:n,page:l,pageClick:r,className:o}=e;const i=b()("wp-block activitypub-pager",o,{current:a});return(0,t.createElement)("a",{className:i,onClick:e=>{e.preventDefault(),!a&&r(l)}},n)}const d={outlined:"outlined",minimal:"minimal"};function f(e){let{compact:a,nextLabel:n,page:l,pageClick:r,perPage:o,prevLabel:i,total:c,variant:s=d.outlined}=e;const p=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,n)=>e>=1&&e<=t&&n.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(l,Math.ceil(c/o)),u=b()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${s}`,{"is-compact":a});return(0,t.createElement)("nav",{className:u},i&&(0,t.createElement)(w,{key:"prev",page:l-1,pageClick:r,active:1===l,"aria-label":i,className:"wp-block-query-pagination-previous block-editor-block-list__block"},i),!a&&(0,t.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},p.map((e=>(0,t.createElement)(w,{key:e,page:e,pageClick:r,active:e===l,className:"page-numbers"},e)))),n&&(0,t.createElement)(w,{key:"next",page:l+1,pageClick:r,active:l===Math.ceil(c/o),"aria-label":n,className:"wp-block-query-pagination-next block-editor-block-list__block"},n))}const{namespace:g}=window._activityPubOptions;function y(e){let{selectedUser:a,per_page:n,order:l,title:o,page:i,setPage:p,className:m="",followLinks:b=!0,followerData:w=!1}=e;const d="site"===a?0:a,[y,k]=(0,s.useState)([]),[E,_]=(0,s.useState)(0),[x,C]=(0,s.useState)(0),[S,O]=function(){const[e,t]=(0,s.useState)(1);return[e,t]}(),N=i||S,P=p||O,L=(0,t.createInterpolateElement)(/* translators: arrow for previous followers link */
(0,c.__)("<span>←</span> Less","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),j=(0,t.createInterpolateElement)(/* translators: arrow for next followers link */
(0,c.__)("More <span>→</span>","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),M=(e,t)=>{k(e),C(t),_(Math.ceil(t/n))};return(0,s.useEffect)((()=>{if(w&&1===N)return M(w.followers,w.total);const e=function(e,t,a,n){const l=`/${g}/users/${e}/followers`,r={per_page:t,order:a,page:n,context:"full"};return(0,v.addQueryArgs)(l,r)}(d,n,l,N);u()({path:e}).then((e=>M(e.orderedItems,e.totalItems))).catch((()=>{}))}),[d,n,l,N,w]),(0,t.createElement)("div",{className:"activitypub-follower-block "+m},(0,t.createElement)("h3",null,o),(0,t.createElement)("ul",null,y&&y.map((e=>(0,t.createElement)("li",{key:e.url},(0,t.createElement)(h,r({},e,{followLinks:b})))))),E>1&&(0,t.createElement)(f,{page:N,perPage:n,total:x,pageClick:P,nextLabel:j,prevLabel:L,compact:"is-style-compact"===m}))}function h(e){let{name:a,icon:n,url:l,preferredUsername:i,followLinks:c=!0}=e;const s=`@${i}`,p={};return c||(p.onClick=e=>e.preventDefault()),(0,t.createElement)(o.ExternalLink,r({className:"activitypub-link",href:l,title:s},p),(0,t.createElement)("img",{width:"40",height:"40",src:n.url,class:"avatar activitypub-avatar",alt:a}),(0,t.createElement)("span",{class:"activitypub-actor"},(0,t.createElement)("strong",{className:"activitypub-name"},a),(0,t.createElement)("span",{class:"sep"},"/"),(0,t.createElement)("span",{class:"activitypub-handle"},s)))}const k=window.wp.data,E=window._activityPubOptions?.enabled;(0,e.registerBlockType)("activitypub/followers",{edit:function(e){let{attributes:a,setAttributes:n}=e;const{order:l,per_page:s,selectedUser:p,title:u}=a,v=(0,i.useBlockProps)(),[m,b]=(0,t.useState)(1),w=[{label:(0,c.__)("New to old","activitypub"),value:"desc"},{label:(0,c.__)("Old to new","activitypub"),value:"asc"}],d=function(){const e=E?.users?(0,k.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,t.useMemo)((()=>{if(!e)return[];const t=E?.site?[{label:(0,c.__)("Whole Site","activitypub"),value:"site"}]:[];return e.reduce(((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e)),t)}),[e])}(),f=e=>t=>{b(1),n({[e]:t})};return(0,t.useEffect)((()=>{d.length&&(d.find((e=>{let{value:t}=e;return t===p}))||n({selectedUser:d[0].value}))}),[p,d]),(0,t.createElement)("div",v,(0,t.createElement)(i.InspectorControls,{key:"setting"},(0,t.createElement)(o.PanelBody,{title:(0,c.__)("Followers Options","activitypub")},(0,t.createElement)(o.TextControl,{label:(0,c.__)("Title","activitypub"),help:(0,c.__)("Title to display above the list of followers. Blank for none.","activitypub"),value:u,onChange:e=>n({title:e})}),d.length>1&&(0,t.createElement)(o.SelectControl,{label:(0,c.__)("Select User","activitypub"),value:p,options:d,onChange:f("selectedUser")}),(0,t.createElement)(o.SelectControl,{label:(0,c.__)("Sort","activitypub"),value:l,options:w,onChange:f("order")}),(0,t.createElement)(o.RangeControl,{label:(0,c.__)("Number of Followers","activitypub"),value:s,onChange:f("per_page"),min:1,max:10}))),(0,t.createElement)(y,r({},a,{page:m,setPage:b,followLinks:!1})))},save:()=>null,icon:l})})()})();
(()=>{var e={184:(e,t)=>{var a;!function(){"use strict";var l={}.hasOwnProperty;function n(){for(var e=[],t=0;t<arguments.length;t++){var a=arguments[t];if(a){var r=typeof a;if("string"===r||"number"===r)e.push(a);else if(Array.isArray(a)){if(a.length){var o=n.apply(null,a);o&&e.push(o)}}else if("object"===r){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){e.push(a.toString());continue}for(var i in a)l.call(a,i)&&a[i]&&e.push(i)}}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):void 0===(a=function(){return n}.apply(t,[]))||(e.exports=a)}()}},t={};function a(l){var n=t[l];if(void 0!==n)return n.exports;var r=t[l]={exports:{}};return e[l](r,r.exports,a),r.exports}a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var l in t)a.o(t,l)&&!a.o(e,l)&&Object.defineProperty(e,l,{enumerable:!0,get:t[l]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";const e=window.wp.blocks,t=window.wp.element,l=window.wp.primitives,n=(0,t.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,t.createElement)(l.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),r=window.wp.components,o=window.wp.blockEditor,i=window.wp.i18n,c=window.React,s=window.wp.apiFetch;var p=a.n(s);const u=window.wp.url;var v=a(184),m=a.n(v);function w({active:e,children:a,page:l,pageClick:n,className:r}){const o=m()("wp-block activitypub-pager",r,{current:e});return(0,t.createElement)("a",{className:o,onClick:t=>{t.preventDefault(),!e&&n(l)}},a)}const b={outlined:"outlined",minimal:"minimal"};function d({compact:e,nextLabel:a,page:l,pageClick:n,perPage:r,prevLabel:o,total:i,variant:c=b.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,l)=>e>=1&&e<=t&&l.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(l,Math.ceil(i/r)),p=m()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,t.createElement)("nav",{className:p},o&&(0,t.createElement)(w,{key:"prev",page:l-1,pageClick:n,active:1===l,"aria-label":o,className:"wp-block-query-pagination-previous block-editor-block-list__block"},o),!e&&(0,t.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,t.createElement)(w,{key:e,page:e,pageClick:n,active:e===l,className:"page-numbers"},e)))),a&&(0,t.createElement)(w,{key:"next",page:l+1,pageClick:n,active:l===Math.ceil(i/r),"aria-label":a,className:"wp-block-query-pagination-next block-editor-block-list__block"},a))}const{namespace:g}=window._activityPubOptions;function f({selectedUser:e,per_page:a,order:l,title:n,page:r,setPage:o,className:s="",followLinks:v=!0,followerData:m=!1}){const w="site"===e?0:e,[b,f]=(0,c.useState)([]),[h,k]=(0,c.useState)(0),[E,_]=(0,c.useState)(0),[x,C]=function(){const[e,t]=(0,c.useState)(1);return[e,t]}(),S=r||x,N=o||C,P=(0,t.createInterpolateElement)(/* translators: arrow for previous followers link */
(0,i.__)("<span>←</span> Less","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,t.createInterpolateElement)(/* translators: arrow for next followers link */
(0,i.__)("More <span>→</span>","activitypub"),{span:(0,t.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),O=(e,t)=>{f(e),_(t),k(Math.ceil(t/a))};return(0,c.useEffect)((()=>{if(m&&1===S)return O(m.followers,m.total);const e=function(e,t,a,l){const n=`/${g}/users/${e}/followers`,r={per_page:t,order:a,page:l,context:"full"};return(0,u.addQueryArgs)(n,r)}(w,a,l,S);p()({path:e}).then((e=>O(e.orderedItems,e.totalItems))).catch((()=>{}))}),[w,a,l,S,m]),(0,t.createElement)("div",{className:"activitypub-follower-block "+s},(0,t.createElement)("h3",null,n),(0,t.createElement)("ul",null,b&&b.map((e=>(0,t.createElement)("li",{key:e.url},(0,t.createElement)(y,{...e,followLinks:v}))))),h>1&&(0,t.createElement)(d,{page:S,perPage:a,total:E,pageClick:N,nextLabel:L,prevLabel:P,compact:"is-style-compact"===s}))}function y({name:e,icon:a,url:l,preferredUsername:n,followLinks:o=!0}){const i=`@${n}`,c={};return o||(c.onClick=e=>e.preventDefault()),(0,t.createElement)(r.ExternalLink,{className:"activitypub-link",href:l,title:i,...c},(0,t.createElement)("img",{width:"40",height:"40",src:a.url,class:"avatar activitypub-avatar",alt:e}),(0,t.createElement)("span",{class:"activitypub-actor"},(0,t.createElement)("strong",{className:"activitypub-name"},e),(0,t.createElement)("span",{class:"sep"},"/"),(0,t.createElement)("span",{class:"activitypub-handle"},i)))}const h=window.wp.data,k=window._activityPubOptions?.enabled;(0,e.registerBlockType)("activitypub/followers",{edit:function({attributes:e,setAttributes:a}){const{order:l,per_page:n,selectedUser:c,title:s}=e,p=(0,o.useBlockProps)(),[u,v]=(0,t.useState)(1),m=[{label:(0,i.__)("New to old","activitypub"),value:"desc"},{label:(0,i.__)("Old to new","activitypub"),value:"asc"}],w=function(){const e=k?.users?(0,h.useSelect)((e=>e("core").getUsers({who:"authors"}))):[];return(0,t.useMemo)((()=>{if(!e)return[];const t=k?.site?[{label:(0,i.__)("Whole Site","activitypub"),value:"site"}]:[];return e.reduce(((e,t)=>(e.push({label:t.name,value:`${t.id}`}),e)),t)}),[e])}(),b=e=>t=>{v(1),a({[e]:t})};return(0,t.useEffect)((()=>{w.length&&(w.find((({value:e})=>e===c))||a({selectedUser:w[0].value}))}),[c,w]),(0,t.createElement)("div",{...p},(0,t.createElement)(o.InspectorControls,{key:"setting"},(0,t.createElement)(r.PanelBody,{title:(0,i.__)("Followers Options","activitypub")},(0,t.createElement)(r.TextControl,{label:(0,i.__)("Title","activitypub"),help:(0,i.__)("Title to display above the list of followers. Blank for none.","activitypub"),value:s,onChange:e=>a({title:e})}),w.length>1&&(0,t.createElement)(r.SelectControl,{label:(0,i.__)("Select User","activitypub"),value:c,options:w,onChange:b("selectedUser")}),(0,t.createElement)(r.SelectControl,{label:(0,i.__)("Sort","activitypub"),value:l,options:m,onChange:b("order")}),(0,t.createElement)(r.RangeControl,{label:(0,i.__)("Number of Followers","activitypub"),value:n,onChange:b("per_page"),min:1,max:10}))),(0,t.createElement)(f,{...e,page:u,setPage:v,followLinks:!1}))},save:()=>null,icon:n})})()})();

View File

@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '04e51e7562fe28b0b2c3');
<?php return array( 'dependencies' => array( 'react', 'wp-api-fetch', 'wp-components', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url' ), 'version' => 'ed5a13e66f8b10323435' );

View File

@ -1,3 +1,3 @@
(()=>{var e,t={189:(e,t,a)=>{"use strict";const r=window.wp.element;function n(){return n=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var a=arguments[t];for(var r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r])}return e},n.apply(this,arguments)}const l=window.React,o=window.wp.apiFetch;var i=a.n(o);const c=window.wp.url,s=window.wp.i18n;var p=a(184),u=a.n(p);function m(e){let{active:t,children:a,page:n,pageClick:l,className:o}=e;const i=u()("wp-block activitypub-pager",o,{current:t});return(0,r.createElement)("a",{className:i,onClick:e=>{e.preventDefault(),!t&&l(n)}},a)}const v={outlined:"outlined",minimal:"minimal"};function f(e){let{compact:t,nextLabel:a,page:n,pageClick:l,perPage:o,prevLabel:i,total:c,variant:s=v.outlined}=e;const p=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,r)=>e>=1&&e<=t&&r.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(n,Math.ceil(c/o)),f=u()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${s}`,{"is-compact":t});return(0,r.createElement)("nav",{className:f},i&&(0,r.createElement)(m,{key:"prev",page:n-1,pageClick:l,active:1===n,"aria-label":i,className:"wp-block-query-pagination-previous block-editor-block-list__block"},i),!t&&(0,r.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},p.map((e=>(0,r.createElement)(m,{key:e,page:e,pageClick:l,active:e===n,className:"page-numbers"},e)))),a&&(0,r.createElement)(m,{key:"next",page:n+1,pageClick:l,active:n===Math.ceil(c/o),"aria-label":a,className:"wp-block-query-pagination-next block-editor-block-list__block"},a))}const b=window.wp.components,{namespace:d}=window._activityPubOptions;function w(e){let{selectedUser:t,per_page:a,order:o,title:p,page:u,setPage:m,className:v="",followLinks:b=!0,followerData:w=!1}=e;const y="site"===t?0:t,[k,h]=(0,l.useState)([]),[E,O]=(0,l.useState)(0),[x,_]=(0,l.useState)(0),[N,j]=function(){const[e,t]=(0,l.useState)(1);return[e,t]}(),S=u||N,C=m||j,L=(0,r.createInterpolateElement)(/* translators: arrow for previous followers link */
(0,s.__)("<span>←</span> Less","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),q=(0,r.createInterpolateElement)(/* translators: arrow for next followers link */
(0,s.__)("More <span>→</span>","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),P=(e,t)=>{h(e),_(t),O(Math.ceil(t/a))};return(0,l.useEffect)((()=>{if(w&&1===S)return P(w.followers,w.total);const e=function(e,t,a,r){const n=`/${d}/users/${e}/followers`,l={per_page:t,order:a,page:r,context:"full"};return(0,c.addQueryArgs)(n,l)}(y,a,o,S);i()({path:e}).then((e=>P(e.orderedItems,e.totalItems))).catch((()=>{}))}),[y,a,o,S,w]),(0,r.createElement)("div",{className:"activitypub-follower-block "+v},(0,r.createElement)("h3",null,p),(0,r.createElement)("ul",null,k&&k.map((e=>(0,r.createElement)("li",{key:e.url},(0,r.createElement)(g,n({},e,{followLinks:b})))))),E>1&&(0,r.createElement)(f,{page:S,perPage:a,total:x,pageClick:C,nextLabel:q,prevLabel:L,compact:"is-style-compact"===v}))}function g(e){let{name:t,icon:a,url:l,preferredUsername:o,followLinks:i=!0}=e;const c=`@${o}`,s={};return i||(s.onClick=e=>e.preventDefault()),(0,r.createElement)(b.ExternalLink,n({className:"activitypub-link",href:l,title:c},s),(0,r.createElement)("img",{width:"40",height:"40",src:a.url,class:"avatar activitypub-avatar",alt:t}),(0,r.createElement)("span",{class:"activitypub-actor"},(0,r.createElement)("strong",{className:"activitypub-name"},t),(0,r.createElement)("span",{class:"sep"},"/"),(0,r.createElement)("span",{class:"activitypub-handle"},c)))}const y=window.wp.domReady;a.n(y)()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follower-block"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,r.render)((0,r.createElement)(w,t),e)}))}))},184:(e,t)=>{var a;!function(){"use strict";var r={}.hasOwnProperty;function n(){for(var e=[],t=0;t<arguments.length;t++){var a=arguments[t];if(a){var l=typeof a;if("string"===l||"number"===l)e.push(a);else if(Array.isArray(a)){if(a.length){var o=n.apply(null,a);o&&e.push(o)}}else if("object"===l){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){e.push(a.toString());continue}for(var i in a)r.call(a,i)&&a[i]&&e.push(i)}}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):void 0===(a=function(){return n}.apply(t,[]))||(e.exports=a)}()}},a={};function r(e){var n=a[e];if(void 0!==n)return n.exports;var l=a[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,a,n,l)=>{if(!a){var o=1/0;for(p=0;p<e.length;p++){for(var[a,n,l]=e[p],i=!0,c=0;c<a.length;c++)(!1&l||o>=l)&&Object.keys(r.O).every((e=>r.O[e](a[c])))?a.splice(c--,1):(i=!1,l<o&&(o=l));if(i){e.splice(p--,1);var s=n();void 0!==s&&(t=s)}}return t}l=l||0;for(var p=e.length;p>0&&e[p-1][2]>l;p--)e[p]=e[p-1];e[p]=[a,n,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var a in t)r.o(t,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={638:0,962:0};r.O.j=t=>0===e[t];var t=(t,a)=>{var n,l,[o,i,c]=a,s=0;if(o.some((t=>0!==e[t]))){for(n in i)r.o(i,n)&&(r.m[n]=i[n]);if(c)var p=c(r)}for(t&&t(a);s<o.length;s++)l=o[s],r.o(e,l)&&e[l]&&e[l][0](),e[l]=0;return r.O(p)},a=globalThis.webpackChunkwordpress_activitypub=globalThis.webpackChunkwordpress_activitypub||[];a.forEach(t.bind(null,0)),a.push=t.bind(null,a.push.bind(a))})();var n=r.O(void 0,[962],(()=>r(189)));n=r.O(n)})();
(()=>{var e,t={142:(e,t,a)=>{"use strict";const r=window.wp.element,n=window.React,l=window.wp.apiFetch;var i=a.n(l);const o=window.wp.url,c=window.wp.i18n;var s=a(184),p=a.n(s);function u({active:e,children:t,page:a,pageClick:n,className:l}){const i=p()("wp-block activitypub-pager",l,{current:e});return(0,r.createElement)("a",{className:i,onClick:t=>{t.preventDefault(),!e&&n(a)}},t)}const m={outlined:"outlined",minimal:"minimal"};function f({compact:e,nextLabel:t,page:a,pageClick:n,perPage:l,prevLabel:i,total:o,variant:c=m.outlined}){const s=((e,t)=>{let a=[1,e-2,e-1,e,e+1,e+2,t];a.sort(((e,t)=>e-t)),a=a.filter(((e,a,r)=>e>=1&&e<=t&&r.lastIndexOf(e)===a));for(let e=a.length-2;e>=0;e--)a[e]===a[e+1]&&a.splice(e+1,1);return a})(a,Math.ceil(o/l)),f=p()("alignwide wp-block-query-pagination is-content-justification-space-between is-layout-flex wp-block-query-pagination-is-layout-flex",`is-${c}`,{"is-compact":e});return(0,r.createElement)("nav",{className:f},i&&(0,r.createElement)(u,{key:"prev",page:a-1,pageClick:n,active:1===a,"aria-label":i,className:"wp-block-query-pagination-previous block-editor-block-list__block"},i),!e&&(0,r.createElement)("div",{className:"block-editor-block-list__block wp-block wp-block-query-pagination-numbers"},s.map((e=>(0,r.createElement)(u,{key:e,page:e,pageClick:n,active:e===a,className:"page-numbers"},e)))),t&&(0,r.createElement)(u,{key:"next",page:a+1,pageClick:n,active:a===Math.ceil(o/l),"aria-label":t,className:"wp-block-query-pagination-next block-editor-block-list__block"},t))}const v=window.wp.components,{namespace:d}=window._activityPubOptions;function b({selectedUser:e,per_page:t,order:a,title:l,page:s,setPage:p,className:u="",followLinks:m=!0,followerData:v=!1}){const b="site"===e?0:e,[g,k]=(0,n.useState)([]),[y,h]=(0,n.useState)(0),[E,x]=(0,n.useState)(0),[_,O]=function(){const[e,t]=(0,n.useState)(1);return[e,t]}(),N=s||_,S=p||O,C=(0,r.createInterpolateElement)(/* translators: arrow for previous followers link */
(0,c.__)("<span>←</span> Less","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-previous-arrow is-arrow-arrow","aria-hidden":"true"})}),L=(0,r.createInterpolateElement)(/* translators: arrow for next followers link */
(0,c.__)("More <span>→</span>","activitypub"),{span:(0,r.createElement)("span",{class:"wp-block-query-pagination-next-arrow is-arrow-arrow","aria-hidden":"true"})}),j=(e,a)=>{k(e),x(a),h(Math.ceil(a/t))};return(0,n.useEffect)((()=>{if(v&&1===N)return j(v.followers,v.total);const e=function(e,t,a,r){const n=`/${d}/users/${e}/followers`,l={per_page:t,order:a,page:r,context:"full"};return(0,o.addQueryArgs)(n,l)}(b,t,a,N);i()({path:e}).then((e=>j(e.orderedItems,e.totalItems))).catch((()=>{}))}),[b,t,a,N,v]),(0,r.createElement)("div",{className:"activitypub-follower-block "+u},(0,r.createElement)("h3",null,l),(0,r.createElement)("ul",null,g&&g.map((e=>(0,r.createElement)("li",{key:e.url},(0,r.createElement)(w,{...e,followLinks:m}))))),y>1&&(0,r.createElement)(f,{page:N,perPage:t,total:E,pageClick:S,nextLabel:L,prevLabel:C,compact:"is-style-compact"===u}))}function w({name:e,icon:t,url:a,preferredUsername:n,followLinks:l=!0}){const i=`@${n}`,o={};return l||(o.onClick=e=>e.preventDefault()),(0,r.createElement)(v.ExternalLink,{className:"activitypub-link",href:a,title:i,...o},(0,r.createElement)("img",{width:"40",height:"40",src:t.url,class:"avatar activitypub-avatar",alt:e}),(0,r.createElement)("span",{class:"activitypub-actor"},(0,r.createElement)("strong",{className:"activitypub-name"},e),(0,r.createElement)("span",{class:"sep"},"/"),(0,r.createElement)("span",{class:"activitypub-handle"},i)))}const g=window.wp.domReady;a.n(g)()((()=>{[].forEach.call(document.querySelectorAll(".activitypub-follower-block"),(e=>{const t=JSON.parse(e.dataset.attrs);(0,r.render)((0,r.createElement)(b,{...t}),e)}))}))},184:(e,t)=>{var a;!function(){"use strict";var r={}.hasOwnProperty;function n(){for(var e=[],t=0;t<arguments.length;t++){var a=arguments[t];if(a){var l=typeof a;if("string"===l||"number"===l)e.push(a);else if(Array.isArray(a)){if(a.length){var i=n.apply(null,a);i&&e.push(i)}}else if("object"===l){if(a.toString!==Object.prototype.toString&&!a.toString.toString().includes("[native code]")){e.push(a.toString());continue}for(var o in a)r.call(a,o)&&a[o]&&e.push(o)}}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):void 0===(a=function(){return n}.apply(t,[]))||(e.exports=a)}()}},a={};function r(e){var n=a[e];if(void 0!==n)return n.exports;var l=a[e]={exports:{}};return t[e](l,l.exports,r),l.exports}r.m=t,e=[],r.O=(t,a,n,l)=>{if(!a){var i=1/0;for(p=0;p<e.length;p++){a=e[p][0],n=e[p][1],l=e[p][2];for(var o=!0,c=0;c<a.length;c++)(!1&l||i>=l)&&Object.keys(r.O).every((e=>r.O[e](a[c])))?a.splice(c--,1):(o=!1,l<i&&(i=l));if(o){e.splice(p--,1);var s=n();void 0!==s&&(t=s)}}return t}l=l||0;for(var p=e.length;p>0&&e[p-1][2]>l;p--)e[p]=e[p-1];e[p]=[a,n,l]},r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var a in t)r.o(t,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={638:0,962:0};r.O.j=t=>0===e[t];var t=(t,a)=>{var n,l,i=a[0],o=a[1],c=a[2],s=0;if(i.some((t=>0!==e[t]))){for(n in o)r.o(o,n)&&(r.m[n]=o[n]);if(c)var p=c(r)}for(t&&t(a);s<i.length;s++)l=i[s],r.o(e,l)&&e[l]&&e[l][0](),e[l]=0;return r.O(p)},a=self.webpackChunkwordpress_activitypub=self.webpackChunkwordpress_activitypub||[];a.forEach(t.bind(null,0)),a.push=t.bind(null,a.push.bind(a))})();var n=r.O(void 0,[962],(()=>r(142)));n=r.O(n)})();

View File

@ -20,13 +20,13 @@ class Activity extends Base_Object {
const CONTEXT = array(
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1',
'https://purl.archive.org/socialweb/webfinger',
array(
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
'PropertyValue' => 'schema:PropertyValue',
'schema' => 'http://schema.org#',
'pt' => 'https://joinpeertube.org/ns#',
'toot' => 'http://joinmastodon.org/ns#',
'webfinger' => 'https://webfinger.net/#',
'litepub' => 'http://litepub.social/ns#',
'lemmy' => 'https://join-lemmy.org/ns#',
'value' => 'schema:value',
@ -51,7 +51,6 @@ class Activity extends Base_Object {
'discoverable' => 'toot:discoverable',
'indexable' => 'toot:indexable',
'sensitive' => 'as:sensitive',
'resource' => 'webfinger:resource',
),
);
@ -196,7 +195,7 @@ class Activity extends Base_Object {
public function set_object( $object ) {
// convert array to object
if ( is_array( $object ) ) {
$object = Base_Object::init_from_array( $object );
$object = self::init_from_array( $object );
}
// set object

View File

@ -254,19 +254,6 @@ class Base_Object {
*/
protected $published;
/**
* A Collection containing objects considered to be responses to
* this object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies
*
* @var string
* | Collection
* | Link
* | null
*/
protected $replies;
/**
* The date and time describing the actual or expected starting time
* of the object.
@ -437,6 +424,19 @@ class Base_Object {
*/
protected $source;
/**
* A Collection containing objects considered to be responses to
* this object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies
*
* @var string
* | Collection
* | Link
* | null
*/
protected $replies;
/**
* Magic function to implement getter and setter
*
@ -671,8 +671,25 @@ class Base_Object {
* @return string The JSON string.
*/
public function to_json() {
$array = $this->to_array();
$array = $this->to_array();
$options = \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
return \wp_json_encode( $array, \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT );
/*
* Options to be passed to json_encode()
*
* @param int $options The current options flags
*/
$options = \apply_filters( 'activitypub_json_encode_options', $options );
return \wp_json_encode( $array, $options );
}
/**
* Returns the keys of the object vars.
*
* @return array The keys of the object vars.
*/
public function get_object_var_keys() {
return \array_keys( \get_object_vars( $this ) );
}
}

View File

@ -2,10 +2,13 @@
namespace Activitypub;
use WP_Post;
use WP_Comment;
use Activitypub\Activity\Activity;
use Activitypub\Collection\Users;
use Activitypub\Collection\Followers;
use Activitypub\Transformer\Factory;
use Activitypub\Transformer\Post;
use Activitypub\Transformer\Comment;
use function Activitypub\is_single_user;
use function Activitypub\is_user_disabled;
@ -25,17 +28,18 @@ class Activity_Dispatcher {
public static function init() {
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity' ), 10, 2 );
\add_action( 'activitypub_send_activity', array( self::class, 'send_activity_or_announce' ), 10, 2 );
\add_action( 'activitypub_send_update_profile_activity', array( self::class, 'send_profile_update' ), 10, 1 );
}
/**
* Send Activities to followers and mentioned users or `Announce` (boost) a blog post.
*
* @param WP_Post $wp_post The ActivityPub Post.
* @param string $type The Activity-Type.
* @param mixed $wp_object The ActivityPub Post.
* @param string $type The Activity-Type.
*
* @return void
*/
public static function send_activity_or_announce( WP_Post $wp_post, $type ) {
public static function send_activity_or_announce( $wp_object, $type ) {
// check if a migration is needed before sending new posts
Migration::maybe_migrate();
@ -43,56 +47,48 @@ class Activity_Dispatcher {
return;
}
$wp_post->post_author = Users::BLOG_USER_ID;
if ( is_single_user() ) {
self::send_activity( $wp_post, $type );
self::send_activity( $wp_object, $type, Users::BLOG_USER_ID );
} else {
self::send_announce( $wp_post, $type );
self::send_announce( $wp_object, $type );
}
}
/**
* Send Activities to followers and mentioned users.
*
* @param WP_Post $wp_post The ActivityPub Post.
* @param string $type The Activity-Type.
* @param mixed $wp_object The ActivityPub Post.
* @param string $type The Activity-Type.
*
* @return void
*/
public static function send_activity( WP_Post $wp_post, $type ) {
if ( is_user_disabled( $wp_post->post_author ) ) {
public static function send_activity( $wp_object, $type, $user_id = null ) {
$transformer = Factory::get_transformer( $wp_object );
if ( null !== $user_id ) {
$transformer->change_wp_user_id( $user_id );
}
$user_id = $transformer->get_wp_user_id();
if ( is_user_disabled( $user_id ) ) {
return;
}
$object = Post::transform( $wp_post )->to_object();
$activity = $transformer->to_activity( $type );
$activity = new Activity();
$activity->set_type( $type );
$activity->set_object( $object );
$follower_inboxes = Followers::get_inboxes( $wp_post->post_author );
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
$inboxes = array_unique( $inboxes );
$json = $activity->to_json();
foreach ( $inboxes as $inbox ) {
safe_remote_post( $inbox, $json, $wp_post->post_author );
}
self::send_activity_to_inboxes( $activity, $user_id );
}
/**
* Send Announces to followers and mentioned users.
*
* @param WP_Post $wp_post The ActivityPub Post.
* @param string $type The Activity-Type.
* @param mixed $wp_object The ActivityPub Post.
* @param string $type The Activity-Type.
*
* @return void
*/
public static function send_announce( WP_Post $wp_post, $type ) {
public static function send_announce( $wp_object, $type ) {
if ( ! in_array( $type, array( 'Create', 'Update' ), true ) ) {
return;
}
@ -101,25 +97,70 @@ class Activity_Dispatcher {
return;
}
$object = Post::transform( $wp_post )->to_object();
$transformer = Factory::get_transformer( $wp_object );
$transformer->change_wp_user_id( Users::BLOG_USER_ID );
$user_id = $transformer->get_wp_user_id();
$activity = $transformer->to_activity( 'Announce' );
self::send_activity_to_inboxes( $activity, $user_id );
}
/**
* Send a "Update" Activity when a user updates their profile.
*
* @param int $user_id The user ID to send an update for.
*
* @return void
*/
public static function send_profile_update( $user_id ) {
$user = Users::get_by_various( $user_id );
// bail if that's not a good user
if ( is_wp_error( $user ) ) {
return;
}
// build the update
$activity = new Activity();
$activity->set_type( 'Announce' );
// to pre-fill attributes like "published" and "id"
$activity->set_object( $object );
// send only the id
$activity->set_object( $object->get_id() );
$activity->set_id( $user->get_url() . '#update' );
$activity->set_type( 'Update' );
$activity->set_actor( $user->get_url() );
$activity->set_object( $user->get_url() );
$activity->set_to( 'https://www.w3.org/ns/activitystreams#Public' );
$follower_inboxes = Followers::get_inboxes( $wp_post->post_author );
$mentioned_inboxes = Mention::get_inboxes( $activity->get_cc() );
// send the update
self::send_activity_to_inboxes( $activity, $user_id );
}
/**
* Send an Activity to all followers and mentioned users.
*
* @param Activity $activity The ActivityPub Activity.
* @param int $user_id The user ID.
*
* @return void
*/
private static function send_activity_to_inboxes( $activity, $user_id ) {
$follower_inboxes = Followers::get_inboxes( $user_id );
$mentioned_inboxes = array();
$cc = $activity->get_cc();
if ( $cc ) {
$mentioned_inboxes = Mention::get_inboxes( $cc );
}
$inboxes = array_merge( $follower_inboxes, $mentioned_inboxes );
$inboxes = array_unique( $inboxes );
if ( empty( $inboxes ) ) {
return;
}
$json = $activity->to_json();
foreach ( $inboxes as $inbox ) {
safe_remote_post( $inbox, $json, $wp_post->post_author );
safe_remote_post( $inbox, $json, $user_id );
}
}
}

View File

@ -7,6 +7,8 @@ use Activitypub\Collection\Users;
use Activitypub\Collection\Followers;
use function Activitypub\sanitize_url;
use function Activitypub\is_comment;
use function Activitypub\is_activitypub_request;
/**
* ActivityPub Class
@ -19,6 +21,7 @@ class Activitypub {
*/
public static function init() {
\add_filter( 'template_include', array( self::class, 'render_json_template' ), 99 );
\add_action( 'template_redirect', array( self::class, 'template_redirect' ) );
\add_filter( 'query_vars', array( self::class, 'add_query_vars' ) );
\add_filter( 'pre_get_avatar_data', array( self::class, 'pre_get_avatar_data' ), 11, 2 );
\add_filter( 'get_comment_link', array( self::class, 'remote_comment_link' ), 11, 3 );
@ -39,6 +42,8 @@ class Activitypub {
\add_action( 'in_plugin_update_message-' . ACTIVITYPUB_PLUGIN_BASENAME, array( self::class, 'plugin_update_message' ) );
\add_filter( 'comment_class', array( self::class, 'comment_class' ), 10, 3 );
// register several post_types
self::register_post_types();
}
@ -50,7 +55,6 @@ class Activitypub {
*/
public static function activate() {
self::flush_rewrite_rules();
Scheduler::register_schedules();
}
@ -98,6 +102,8 @@ class Activitypub {
if ( \is_author() ) {
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/author-json.php';
} elseif ( is_comment() ) {
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/comment-json.php';
} elseif ( \is_singular() ) {
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
} elseif ( \is_home() ) {
@ -115,11 +121,44 @@ class Activitypub {
return $json_template;
}
/**
* Custom redirects for ActivityPub requests.
*
* @return void
*/
public static function template_redirect() {
$comment_id = get_query_var( 'c', null );
// check if it seems to be a comment
if ( ! $comment_id ) {
return;
}
$comment = get_comment( $comment_id );
// load a 404 page if `c` is set but not valid
if ( ! $comment ) {
global $wp_query;
$wp_query->set_404();
return;
}
// stop if it's not an ActivityPub comment
if ( is_activitypub_request() && $comment->user_id ) {
return;
}
wp_safe_redirect( get_comment_link( $comment ) );
exit;
}
/**
* Add the 'activitypub' query variable so WordPress won't mangle it.
*/
public static function add_query_vars( $vars ) {
$vars[] = 'activitypub';
$vars[] = 'c';
$vars[] = 'p';
return $vars;
}
@ -195,10 +234,18 @@ class Activitypub {
* @return string $url
*/
public static function remote_comment_link( $comment_link, $comment ) {
$remote_comment_link = get_comment_meta( $comment->comment_ID, 'source_url', true );
if ( $remote_comment_link ) {
$comment_link = esc_url( $remote_comment_link );
if ( ! $comment || is_admin() ) {
return $comment_link;
}
$comment_meta = \get_comment_meta( $comment->comment_ID );
if ( ! empty( $comment_meta['source_url'][0] ) ) {
return $comment_meta['source_url'][0];
} elseif ( ! empty( $comment_meta['source_id'][0] ) ) {
return $comment_meta['source_id'][0];
}
return $comment_link;
}
@ -341,7 +388,7 @@ class Activitypub {
* @return void
*/
private static function register_post_types() {
register_post_type(
\register_post_type(
Followers::POST_TYPE,
array(
'labels' => array(
@ -358,7 +405,7 @@ class Activitypub {
)
);
register_post_meta(
\register_post_meta(
Followers::POST_TYPE,
'activitypub_inbox',
array(
@ -368,13 +415,13 @@ class Activitypub {
)
);
register_post_meta(
\register_post_meta(
Followers::POST_TYPE,
'activitypub_errors',
array(
'type' => 'string',
'single' => false,
'sanitize_callback' => function( $value ) {
'sanitize_callback' => function ( $value ) {
if ( ! is_string( $value ) ) {
throw new Exception( 'Error message is no valid string' );
}
@ -384,30 +431,48 @@ class Activitypub {
)
);
register_post_meta(
\register_post_meta(
Followers::POST_TYPE,
'activitypub_user_id',
array(
'type' => 'string',
'single' => false,
'sanitize_callback' => function( $value ) {
'sanitize_callback' => function ( $value ) {
return esc_sql( $value );
},
)
);
register_post_meta(
\register_post_meta(
Followers::POST_TYPE,
'activitypub_actor_json',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => function( $value ) {
'sanitize_callback' => function ( $value ) {
return sanitize_text_field( $value );
},
)
);
do_action( 'activitypub_after_register_post_type' );
\do_action( 'activitypub_after_register_post_type' );
}
/**
* Filters the CSS classes to add an ActivityPub class.
*
* @param string[] $classes An array of comment classes.
* @param string[] $css_class An array of additional classes added to the list.
* @param string $comment_id The comment ID as a numeric string.
*
* @return string[] An array of classes.
*/
public static function comment_class( $classes, $css_class, $comment_id ) {
// check if ActivityPub comment
if ( 'activitypub' === get_comment_meta( $comment_id, 'protocol', true ) ) {
$classes[] = 'activitypub-comment';
}
return $classes;
}
}

View File

@ -18,6 +18,7 @@ class Admin {
\add_action( 'admin_init', array( self::class, 'register_settings' ) );
\add_action( 'personal_options_update', array( self::class, 'save_user_description' ) );
\add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
\add_action( 'admin_notices', array( self::class, 'admin_notices' ) );
if ( ! is_user_disabled( get_current_user_id() ) ) {
\add_action( 'show_user_profile', array( self::class, 'add_profile' ) );
@ -46,6 +47,37 @@ class Admin {
}
}
/**
* Display admin menu notices about configuration problems or conflicts.
*
* @return void
*/
public static function admin_notices() {
$permalink_structure = \get_option( 'permalink_structure' );
if ( empty( $permalink_structure ) ) {
$admin_notice = \__( 'You are using the ActivityPub plugin without setting a permalink structure. This will prevent ActivityPub from working. Please set a permalink structure.', 'activitypub' );
self::show_admin_notice( $admin_notice, 'error' );
}
}
/**
* Display one admin menu notice about configuration problems or conflicts.
*
* @param string $admin_notice The notice to display.
* @param string $level The level of the notice (error, warning, success, info).
*
* @return void
*/
private static function show_admin_notice( $admin_notice, $level ) {
?>
<div class="notice notice-<?php echo esc_attr( $level ); ?>">
<p><?php echo wp_kses( $admin_notice, 'data' ); ?></p>
</div>
<?php
}
/**
* Load settings page
*/
@ -172,7 +204,7 @@ class Admin {
'description' => \esc_html__( 'The Identifier of the Blog-User', 'activitypub' ),
'show_in_rest' => true,
'default' => Blog_User::get_default_username(),
'sanitize_callback' => function( $value ) {
'sanitize_callback' => function ( $value ) {
// hack to allow dots in the username
$parts = explode( '.', $value );
$sanitized = array();

View File

@ -3,7 +3,9 @@ namespace Activitypub;
use Activitypub\Collection\Followers;
use Activitypub\Collection\Users as User_Collection;
use Activitypub\is_user_type_disabled;
use function Activitypub\object_to_uri;
use function Activitypub\is_user_type_disabled;
class Blocks {
public static function init() {
@ -73,7 +75,7 @@ class Blocks {
if ( ! is_wp_error( $user ) ) {
$attrs['profileData'] = self::filter_array_by_keys(
$user->to_array(),
array( 'icon', 'name', 'resource' )
array( 'icon', 'name', 'webfinger' )
);
}
$wrapper_attributes = get_block_wrapper_attributes(
@ -94,7 +96,7 @@ class Blocks {
$attrs['followerData']['total'] = $follower_data['total'];
$attrs['followerData']['followers'] = array_map(
function( $follower ) {
function ( $follower ) {
return self::filter_array_by_keys(
$follower->to_array(),
array( 'icon', 'name', 'preferredUsername', 'url' )
@ -140,7 +142,7 @@ class Blocks {
return sprintf(
$template,
esc_url( $data['url'] ),
esc_url( object_to_uri( $data['url'] ) ),
esc_attr( $data['name'] ),
esc_attr( $data['icon']['url'] ),
esc_html( $data['name'] ),

View File

@ -12,8 +12,8 @@ class Hashtag {
*/
public static function init() {
if ( '1' === \get_option( 'activitypub_use_hashtags', '1' ) ) {
\add_filter( 'wp_insert_post', array( self::class, 'insert_post' ), 10, 2 );
\add_filter( 'the_content', array( self::class, 'the_content' ), 10, 2 );
\add_action( 'wp_insert_post', array( self::class, 'insert_post' ), 10, 2 );
\add_filter( 'the_content', array( self::class, 'the_content' ), 10, 1 );
}
}
@ -67,7 +67,7 @@ class Hashtag {
$tag = strtolower( $m[2] );
if ( '/' === $m[1] ) {
// Closing tag.
$i = array_search( $tag, $tag_stack );
$i = array_search( $tag, $tag_stack, true );
// We can only remove the tag from the stack if it is in the stack.
if ( false !== $i ) {
$tag_stack = array_slice( $tag_stack, 0, $i );

View File

@ -39,11 +39,6 @@ class Health_Check {
'test' => array( self::class, 'test_webfinger' ),
);
$tests['direct']['activitypub_test_system_cron'] = array(
'label' => __( 'System Cron Test', 'activitypub' ),
'test' => array( self::class, 'test_system_cron' ),
);
return $tests;
}
@ -257,17 +252,10 @@ class Health_Check {
* @return boolean|WP_Error
*/
public static function is_webfinger_endpoint_accessible() {
$user = \wp_get_current_user();
$user = Users::get_by_id( Users::APPLICATION_USER_ID );
$resource = $user->get_webfinger();
if ( ! is_user_type_disabled( 'blog' ) ) {
$account = get_webfinger_resource( $user->ID );
} elseif ( ! is_user_type_disabled( 'user' ) ) {
$account = get_webfinger_resource( Users::BLOG_USER_ID );
} else {
$account = '';
}
$url = Webfinger::resolve( $account );
$url = Webfinger::resolve( $resource );
if ( \is_wp_error( $url ) ) {
$allowed = array( 'code' => array() );
$not_accessible = wp_kses(

View File

@ -14,7 +14,8 @@ class Mention {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_filter( 'the_content', array( self::class, 'the_content' ), 99, 2 );
\add_filter( 'the_content', array( self::class, 'the_content' ), 99, 1 );
\add_filter( 'comment_text', array( self::class, 'the_content' ), 10, 1 );
\add_filter( 'activitypub_extract_mentions', array( self::class, 'extract_mentions' ), 99, 2 );
}
@ -100,7 +101,14 @@ class Mention {
if ( ! empty( $metadata['preferredUsername'] ) ) {
$username = $metadata['preferredUsername'];
}
return \sprintf( '<a rel="mention" class="u-url mention" href="%s">@<span>%s</span></a>', esc_url( $metadata['url'] ), esc_html( $username ) );
$url = isset( $metadata['url'] ) ? $metadata['url'] : $metadata['url'];
if ( \is_array( $url ) ) {
$url = $url[0];
}
return \sprintf( '<a rel="mention" class="u-url mention" href="%s">@<span>%s</span></a>', esc_url( $url ), esc_html( $username ) );
}
return $result[0];

View File

@ -6,22 +6,77 @@ use Activitypub\Collection\Users;
use Activitypub\Collection\Followers;
use Activitypub\Transformer\Post;
use function Activitypub\is_user_type_disabled;
/**
* ActivityPub Scheduler Class
*
* @author Matthias Pfefferle
*/
class Scheduler {
/**
* Initialize the class, registering WordPress hooks
*/
public static function init() {
// Post transitions
\add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 );
\add_action(
'edit_attachment',
function ( $post_id ) {
self::schedule_post_activity( 'publish', 'publish', $post_id );
}
);
\add_action(
'add_attachment',
function ( $post_id ) {
self::schedule_post_activity( 'publish', '', $post_id );
}
);
\add_action(
'delete_attachment',
function ( $post_id ) {
self::schedule_post_activity( 'trash', '', $post_id );
}
);
// Comment transitions
\add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 );
\add_action(
'edit_comment',
function ( $comment_id ) {
self::schedule_comment_activity( 'approved', 'approved', $comment_id );
}
);
\add_action(
'wp_insert_comment',
function ( $comment_id ) {
self::schedule_comment_activity( 'approved', '', $comment_id );
}
);
// Follower Cleanups
\add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
\add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) );
// Migration
\add_action( 'admin_init', array( self::class, 'schedule_migration' ) );
// profile updates for blog options
if ( ! is_user_type_disabled( 'blog' ) ) {
\add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) );
\add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) );
\add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) );
\add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) );
\add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) );
}
// profile updates for user options
if ( ! is_user_type_disabled( 'user' ) ) {
\add_action( 'wp_update_user', array( self::class, 'user_update' ) );
\add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 );
// @todo figure out a feasible way of updating the header image since it's not unique to any user.
}
}
/**
@ -58,6 +113,8 @@ class Scheduler {
* @param WP_Post $post Post object.
*/
public static function schedule_post_activity( $new_status, $old_status, $post ) {
$post = get_post( $post );
// Do not send activities if post is password protected.
if ( \post_password_required( $post ) ) {
return;
@ -99,6 +156,58 @@ class Scheduler {
);
}
/**
* Schedule Comment Activities
*
* transition_comment_status()
*
* @param string $new_status New comment status.
* @param string $old_status Old comment status.
* @param WP_Comment $comment Comment object.
*/
public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
$comment = get_comment( $comment );
// Federate only approved comments.
if ( ! $comment->user_id ) {
return;
}
if (
'approved' === $new_status &&
'approved' !== $old_status
) {
$type = 'Create';
} elseif ( 'approved' === $new_status ) {
$type = 'Update';
\update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', time(), true );
} elseif (
'trash' === $new_status ||
'spam' === $new_status
) {
$type = 'Delete';
}
if ( ! $type ) {
return;
}
\wp_schedule_single_event(
\time(),
'activitypub_send_activity',
array( $comment, $type )
);
\wp_schedule_single_event(
\time(),
sprintf(
'activitypub_send_%s_activity',
\strtolower( $type )
),
array( $comment )
);
}
/**
* Update followers
*
@ -166,4 +275,68 @@ class Scheduler {
\wp_schedule_single_event( \time(), 'activitypub_schedule_migration' );
}
}
/**
* Send a profile update when relevant user meta is updated.
*
* @param int $meta_id Meta ID being updated.
* @param int $user_id User ID being updated.
* @param string $meta_key Meta key being updated.
*
* @return void
*/
public static function user_meta_update( $meta_id, $user_id, $meta_key ) {
// don't bother if the user can't publish
if ( ! \user_can( $user_id, 'publish_posts' ) ) {
return;
}
// the user meta fields that affect a profile.
$fields = array(
'activitypub_user_description',
'description',
'user_url',
'display_name',
);
if ( in_array( $meta_key, $fields, true ) ) {
self::schedule_profile_update( $user_id );
}
}
/**
* Send a profile update when a user is updated.
*
* @param int $user_id User ID being updated.
*
* @return void
*/
public static function user_update( $user_id ) {
// don't bother if the user can't publish
if ( ! \user_can( $user_id, 'publish_posts' ) ) {
return;
}
self::schedule_profile_update( $user_id );
}
/**
* Theme mods only have a dynamic filter so we fudge it like this.
* @param mixed $value
* @return mixed
*/
public static function blog_user_update( $value = null ) {
self::schedule_profile_update( 0 );
return $value;
}
/**
* Send a profile update to all followers. Gets hooked into all relevant options/meta etc.
* @param int $user_id The user ID to update (Could be 0 for Blog-User).
*/
public static function schedule_profile_update( $user_id ) {
\wp_schedule_single_event(
\time(),
'activitypub_send_update_profile_activity',
array( $user_id )
);
}
}

View File

@ -110,8 +110,13 @@ class Shortcodes {
$excerpt = \get_post_field( 'post_excerpt', $item );
if ( '' === $excerpt ) {
if ( 'attachment' === $item->post_type ) {
// get title of attachment with fallback to alt text.
$content = wp_get_attachment_caption( $item->ID );
if ( empty( $content ) ) {
$content = get_post_meta( $item->ID, '_wp_attachment_image_alt', true );
}
} elseif ( '' === $excerpt ) {
$content = \get_post_field( 'post_content', $item );
// An empty string will make wp_trim_excerpt do stuff we do not want.
@ -127,8 +132,7 @@ class Shortcodes {
// Strip out any remaining tags.
$excerpt = \wp_strip_all_tags( $excerpt );
/** This filter is documented in wp-includes/formatting.php */
$excerpt_more = \apply_filters( 'excerpt_more', ' [...]' );
$excerpt_more = \apply_filters( 'activitypub_excerpt_more', ' [&hellip;]' );
$excerpt_more_len = strlen( $excerpt_more );
// We now have a excerpt, but we need to check it's length, it may be longer than we want for two reasons:
@ -208,20 +212,30 @@ class Shortcodes {
$tag
);
$content = \get_post_field( 'post_content', $item );
$content = '';
if ( 'yes' === $atts['apply_filters'] ) {
$content = \apply_filters( 'the_content', $content );
if ( 'attachment' === $item->post_type ) {
// get title of attachment with fallback to alt text.
$content = wp_get_attachment_caption( $item->ID );
if ( empty( $content ) ) {
$content = get_post_meta( $item->ID, '_wp_attachment_image_alt', true );
}
} else {
$content = do_blocks( $content );
$content = wptexturize( $content );
$content = wp_filter_content_tags( $content );
}
$content = \get_post_field( 'post_content', $item );
// replace script and style elements
$content = \preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $content );
$content = strip_shortcodes( $content );
$content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) );
if ( 'yes' === $atts['apply_filters'] ) {
$content = \apply_filters( 'the_content', $content );
} else {
$content = do_blocks( $content );
$content = wptexturize( $content );
$content = wp_filter_content_tags( $content );
}
// replace script and style elements
$content = \preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $content );
$content = strip_shortcodes( $content );
$content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) );
}
add_shortcode( 'ap_content', array( 'Activitypub\Shortcodes', 'content' ) );

View File

@ -292,7 +292,8 @@ class Signature {
if ( is_array( $headers['digest'] ) ) {
$headers['digest'] = $headers['digest'][0];
}
$digest = explode( '=', $headers['digest'], 2 );
$hashalg = 'sha256';
$digest = explode( '=', $headers['digest'], 2 );
if ( 'SHA-256' === $digest[0] ) {
$hashalg = 'sha256';
}

View File

@ -15,89 +15,81 @@ class Webfinger {
/**
* Returns a users WebFinger "resource"
*
* @param int $user_id
* @param int $user_id The WordPress user id
*
* @return string The user-resource
*/
public static function get_user_resource( $user_id ) {
// use WebFinger plugin if installed
if ( \function_exists( '\get_webfinger_resource' ) ) {
return \get_webfinger_resource( $user_id, false );
}
$user = Users::get_by_id( $user_id );
if ( ! $user || is_wp_error( $user ) ) {
return '';
}
return $user->get_resource();
return $user->get_webfinger();
}
/**
* Resolve a WebFinger resource
*
* @param string $resource The WebFinger resource
* @param string $uri The WebFinger Resource
*
* @return string|WP_Error The URL or WP_Error
*/
public static function resolve( $resource ) {
if ( ! $resource ) {
return null;
public static function resolve( $uri ) {
$data = self::get_data( $uri );
if ( \is_wp_error( $data ) ) {
return $data;
}
if ( ! preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $resource, $m ) ) {
return null;
}
$transient_key = 'activitypub_resolve_' . ltrim( $resource, '@' );
$link = \get_transient( $transient_key );
if ( $link ) {
return $link;
}
$url = \add_query_arg( 'resource', 'acct:' . ltrim( $resource, '@' ), 'https://' . $m[2] . '/.well-known/webfinger' );
if ( ! \wp_http_validate_url( $url ) ) {
$response = new WP_Error( 'invalid_webfinger_url', null, $url );
\set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $response;
}
// try to access author URL
$response = \wp_remote_get(
$url,
array(
'headers' => array( 'Accept' => 'application/jrd+json' ),
'redirection' => 2,
'timeout' => 2,
)
);
if ( \is_wp_error( $response ) ) {
$link = new WP_Error( 'webfinger_url_not_accessible', null, $url );
\set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $link;
}
$body = \wp_remote_retrieve_body( $response );
$body = \json_decode( $body, true );
if ( empty( $body['links'] ) ) {
$link = new WP_Error( 'webfinger_url_invalid_response', null, $url );
\set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $link;
}
foreach ( $body['links'] as $link ) {
if ( 'self' === $link['rel'] && 'application/activity+json' === $link['type'] ) {
\set_transient( $transient_key, $link['href'], WEEK_IN_SECONDS );
foreach ( $data['links'] as $link ) {
if (
'self' === $link['rel'] &&
'application/activity+json' === $link['type']
) {
return $link['href'];
}
}
$link = new WP_Error( 'webfinger_url_no_activitypub', null, $body );
\set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $link;
return new WP_Error( 'webfinger_url_no_activitypub', null, $data );
}
/**
* Transform a URI to an acct <identifier>@<host>
*
* @param string $uri The URI (acct:, mailto:, http:, https:)
*
* @return string|WP_Error Error or acct URI
*/
public static function uri_to_acct( $uri ) {
$data = self::get_data( $uri );
if ( is_wp_error( $data ) ) {
return $data;
}
// check if subject is an acct URI
if (
isset( $data['subject'] ) &&
\str_starts_with( $data['subject'], 'acct:' )
) {
return $data['subject'];
}
// search for an acct URI in the aliases
if ( isset( $data['aliases'] ) ) {
foreach ( $data['aliases'] as $alias ) {
if ( \str_starts_with( $alias, 'acct:' ) ) {
return $alias;
}
}
}
return new WP_Error(
'webfinger_url_no_acct',
__( 'No acct URI found.', 'activitypub' ),
$data
);
}
/**
@ -137,7 +129,7 @@ class Webfinger {
}
if ( empty( $host ) ) {
return new WP_Error( 'invalid_identifier', __( 'Invalid Identifier', 'activitypub' ) );
return new WP_Error( 'webfinger_invalid_identifier', __( 'Invalid Identifier', 'activitypub' ) );
}
return array( $identifier, $host );
@ -146,55 +138,70 @@ class Webfinger {
/**
* Get the WebFinger data for a given URI
*
* @param string $identifier The Identifier: <identifier>@<host>
* @param string $host The Host: <identifier>@<host>
* @param string $uri The Identifier: <identifier>@<host> or URI
*
* @return WP_Error|array Error reaction or array with
* identifier and host as values
*/
public static function get_data( $identifier, $host ) {
$webfinger_url = 'https://' . $host . '/.well-known/webfinger?resource=' . rawurlencode( $identifier );
$response = wp_safe_remote_get(
$webfinger_url,
array(
'headers' => array( 'Accept' => 'application/jrd+json' ),
'redirection' => 0,
'timeout' => 2,
)
);
if ( is_wp_error( $response ) ) {
return new WP_Error( 'webfinger_url_not_accessible', null, $webfinger_url );
}
$body = wp_remote_retrieve_body( $response );
return json_decode( $body, true );
}
/**
* Undocumented function
*
* @return void
*/
public static function get_remote_follow_endpoint( $uri ) {
public static function get_data( $uri ) {
$identifier_and_host = self::get_identifier_and_host( $uri );
if ( is_wp_error( $identifier_and_host ) ) {
return $identifier_and_host;
}
$transient_key = self::generate_cache_key( $uri );
list( $identifier, $host ) = $identifier_and_host;
$data = self::get_data( $identifier, $host );
$data = \get_transient( $transient_key );
if ( $data ) {
return $data;
}
$webfinger_url = 'https://' . $host . '/.well-known/webfinger?resource=' . rawurlencode( $identifier );
$response = wp_safe_remote_get(
$webfinger_url,
array(
'headers' => array( 'Accept' => 'application/jrd+json' ),
)
);
if ( is_wp_error( $response ) ) {
return new WP_Error(
'webfinger_url_not_accessible',
__( 'The WebFinger Resource is not accessible.', 'activitypub' ),
$webfinger_url
);
}
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
\set_transient( $transient_key, $data, WEEK_IN_SECONDS );
return $data;
}
/**
* Get the Remote-Follow endpoint for a given URI
*
* @return string|WP_Error Error or the Remote-Follow endpoint URI.
*/
public static function get_remote_follow_endpoint( $uri ) {
$data = self::get_data( $uri );
if ( is_wp_error( $data ) ) {
return $data;
}
if ( empty( $data['links'] ) ) {
return new WP_Error( 'webfinger_url_invalid_response', null, $data );
return new WP_Error(
'webfinger_missing_links',
__( 'No valid Link elements found.', 'activitypub' ),
$data
);
}
foreach ( $data['links'] as $link ) {
@ -203,6 +210,27 @@ class Webfinger {
}
}
return new WP_Error( 'webfinger_remote_follow_endpoint_invalid', $data, array( 'status' => 417 ) );
return new WP_Error(
'webfinger_missing_remote_follow_endpoint',
__( 'No valid Remote-Follow endpoint found.', 'activitypub' ),
$data
);
}
/**
* Generate a cache key for a given URI
*
* @param string $uri A WebFinger Resource URI
*
* @return string The cache key
*/
public static function generate_cache_key( $uri ) {
$uri = ltrim( $uri, '@' );
if ( filter_var( $uri, FILTER_VALIDATE_EMAIL ) ) {
$uri = 'acct:' . $uri;
}
return 'webfinger_' . md5( $uri );
}
}

View File

@ -182,7 +182,7 @@ class Followers {
$query = new WP_Query( $args );
$total = $query->found_posts;
$followers = array_map(
function( $post ) {
function ( $post ) {
return Follower::init_from_cpt( $post );
},
$query->get_posts()

View File

@ -31,12 +31,13 @@ class Interactions {
return false;
}
$in_reply_to = \esc_url_raw( $activity['object']['inReplyTo'] );
$comment_post_id = \url_to_postid( $in_reply_to );
$parent_comment = object_id_to_comment( $in_reply_to );
$in_reply_to = \esc_url_raw( $activity['object']['inReplyTo'] );
$comment_post_id = \url_to_postid( $in_reply_to );
$parent_comment_id = url_to_commentid( $in_reply_to );
// save only replys and reactions
if ( ! $comment_post_id && $parent_comment ) {
if ( ! $comment_post_id && $parent_comment_id ) {
$parent_comment = get_comment( $parent_comment_id );
$comment_post_id = $parent_comment->comment_post_ID;
}
@ -53,15 +54,14 @@ class Interactions {
$commentdata = array(
'comment_post_ID' => $comment_post_id,
'comment_author' => \esc_attr( $meta['name'] ),
'comment_author' => isset( $meta['name'] ) ? \esc_attr( $meta['name'] ) : \esc_attr( $meta['preferredUsername'] ),
'comment_author_url' => \esc_url_raw( $meta['url'] ),
'comment_content' => \addslashes( $activity['object']['content'] ),
'comment_type' => 'comment',
'comment_author_email' => '',
'comment_parent' => $parent_comment ? $parent_comment->comment_ID : 0,
'comment_parent' => $parent_comment_id ? $parent_comment_id : 0,
'comment_meta' => array(
'source_id' => \esc_url_raw( $activity['object']['id'] ),
'source_url' => \esc_url_raw( $activity['object']['url'] ),
'protocol' => 'activitypub',
),
);
@ -70,6 +70,10 @@ class Interactions {
$commentdata['comment_meta']['avatar_url'] = \esc_url_raw( $meta['icon']['url'] );
}
if ( isset( $activity['object']['url'] ) ) {
$commentdata['comment_meta']['source_url'] = \esc_url_raw( $activity['object']['url'] );
}
// disable flood control
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
// do not require email for AP entries
@ -77,7 +81,7 @@ class Interactions {
// No nonce possible for this submission route
\add_filter(
'akismet_comment_nonce',
function() {
function () {
return 'inactive';
}
);
@ -98,20 +102,20 @@ class Interactions {
*
* @param array $activity The activity-object
*
* @return array|false The commentdata or false on failure
* @return array|string|int|\WP_Error|false The commentdata or false on failure
*/
public static function update_comment( $activity ) {
$meta = get_remote_metadata_by_actor( $activity['actor'] );
//Determine comment_ID
$object_comment_id = url_to_commentid( \esc_url_raw( $activity['object']['id'] ) );
$comment = object_id_to_comment( \esc_url_raw( $activity['object']['id'] ) );
$commentdata = \get_comment( $comment, ARRAY_A );
if ( ! $object_comment_id ) {
if ( ! $commentdata ) {
return false;
}
//found a local comment id
$commentdata = \get_comment( $object_comment_id, ARRAY_A );
$commentdata['comment_author'] = \esc_attr( $meta['name'] ? $meta['name'] : $meta['preferredUsername'] );
$commentdata['comment_content'] = \addslashes( $activity['object']['content'] );
if ( isset( $meta['icon']['url'] ) ) {
@ -125,20 +129,24 @@ class Interactions {
// No nonce possible for this submission route
\add_filter(
'akismet_comment_nonce',
function() {
function () {
return 'inactive';
}
);
\add_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10, 2 );
$comment = \wp_update_comment( $commentdata, true );
$state = \wp_update_comment( $commentdata, true );
\remove_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10 );
\remove_filter( 'pre_option_require_name_email', '__return_false' );
// re-add flood control
\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
return $comment;
if ( 1 === $state ) {
return $commentdata;
} else {
return $state; // Either `false` or a `WP_Error` instance or `0` or `1`!
}
}
/**

View File

@ -7,6 +7,7 @@ use Activitypub\Model\User;
use Activitypub\Model\Blog_User;
use Activitypub\Model\Application_User;
use function Activitypub\url_to_authorid;
use function Activitypub\is_user_disabled;
class Users {
@ -103,6 +104,8 @@ class Users {
return self::get_by_id( $user->results[0] );
}
$username = str_replace( array( '*', '%' ), '', $username );
// check for login or nicename.
$user = new WP_User_Query(
array(
@ -133,29 +136,79 @@ class Users {
* @return \Acitvitypub\Model\User The User.
*/
public static function get_by_resource( $resource ) {
if ( \strpos( $resource, '@' ) === false ) {
return new WP_Error(
'activitypub_unsupported_resource',
\__( 'Resource is invalid', 'activitypub' ),
array( 'status' => 400 )
);
$scheme = 'acct';
$match = array();
// try to extract the scheme and the host
if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $resource, $match ) ) {
// extract the scheme
$scheme = esc_attr( $match[1] );
}
$resource = \str_replace( 'acct:', '', $resource );
switch ( $scheme ) {
// check for http(s) URIs
case 'http':
case 'https':
$url_parts = wp_parse_url( $resource );
$resource_identifier = \substr( $resource, 0, \strrpos( $resource, '@' ) );
$resource_host = self::normalize_host( \substr( \strrchr( $resource, '@' ), 1 ) );
$blog_host = self::normalize_host( \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) );
// check for http(s)://blog.example.com/@username
if (
isset( $url_parts['path'] ) &&
str_starts_with( $url_parts['path'], '/@' )
) {
$identifier = str_replace( '/@', '', $url_parts['path'] );
$identifier = untrailingslashit( $identifier );
if ( $blog_host !== $resource_host ) {
return new WP_Error(
'activitypub_wrong_host',
\__( 'Resource host does not match blog host', 'activitypub' ),
array( 'status' => 404 )
);
return self::get_by_username( $identifier );
}
// check for http(s)://blog.example.com/author/username
$user_id = url_to_authorid( $resource );
if ( $user_id ) {
return self::get_by_id( $user_id );
}
// check for http(s)://blog.example.com/
if (
self::normalize_url( site_url() ) === self::normalize_url( $resource ) ||
self::normalize_url( home_url() ) === self::normalize_url( $resource )
) {
return self::get_by_id( self::BLOG_USER_ID );
}
return new WP_Error(
'activitypub_no_user_found',
\__( 'User not found', 'activitypub' ),
array( 'status' => 404 )
);
// check for acct URIs
case 'acct':
$resource = \str_replace( 'acct:', '', $resource );
$identifier = \substr( $resource, 0, \strrpos( $resource, '@' ) );
$host = self::normalize_host( \substr( \strrchr( $resource, '@' ), 1 ) );
$blog_host = self::normalize_host( \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) );
if ( $blog_host !== $host ) {
return new WP_Error(
'activitypub_wrong_host',
\__( 'Resource host does not match blog host', 'activitypub' ),
array( 'status' => 404 )
);
}
// prepare wildcards https://github.com/mastodon/mastodon/issues/22213
if ( in_array( $identifier, array( '_', '*', '' ), true ) ) {
return self::get_by_id( self::BLOG_USER_ID );
}
return self::get_by_username( $identifier );
default:
return new WP_Error(
'activitypub_wrong_scheme',
\__( 'Wrong scheme', 'activitypub' ),
array( 'status' => 404 )
);
}
return self::get_by_username( $resource_identifier );
}
/**
@ -168,7 +221,12 @@ class Users {
public static function get_by_various( $id ) {
if ( is_numeric( $id ) ) {
return self::get_by_id( $id );
} elseif ( filter_var( $id, FILTER_VALIDATE_URL ) ) {
} elseif (
// is URL
filter_var( $id, FILTER_VALIDATE_URL ) ||
// is acct
str_starts_with( $id, 'acct:' )
) {
return self::get_by_resource( $id );
} else {
return self::get_by_username( $id );
@ -176,7 +234,7 @@ class Users {
}
/**
* Normalize the host.
* Normalize a host.
*
* @param string $host The host.
*
@ -186,6 +244,22 @@ class Users {
return \str_replace( 'www.', '', $host );
}
/**
* Normalize a URL.
*
* @param string $url The URL.
*
* @return string The normalized URL.
*/
public static function normalize_url( $url ) {
$url = \untrailingslashit( $url );
$url = \str_replace( 'https://', '', $url );
$url = \str_replace( 'http://', '', $url );
$url = \str_replace( 'www.', '', $url );
return $url;
}
/**
* Get the User collection.
*

View File

@ -47,3 +47,53 @@ if ( ! function_exists( 'is_countable' ) ) {
return is_array( $value ) || $value instanceof \Countable;
}
}
/**
* Polyfill for `array_is_list()` function added in PHP 7.3.
*
* @param array $array The array to check.
*
* @return bool True if `$array` is a list, otherwise false.
*/
if ( ! function_exists( 'array_is_list' ) ) {
function array_is_list( $array ) {
if ( ! is_array( $array ) ) {
return false;
}
if ( array_values( $array ) === $array ) {
return true;
}
$next_key = -1;
foreach ( $array as $k => $v ) {
if ( ++$next_key !== $k ) {
return false;
}
}
return true;
}
}
if ( ! function_exists( 'str_contains' ) ) {
/**
* Polyfill for `str_contains()` function added in PHP 8.0.
*
* Performs a case-sensitive check indicating if needle is
* contained in haystack.
*
* @param string $haystack The string to search in.
* @param string $needle The substring to search for in the `$haystack`.
*
* @return bool True if `$needle` is in `$haystack`, otherwise false.
*/
function str_contains( $haystack, $needle ) {
if ( '' === $needle ) {
return true;
}
return false !== strpos( $haystack, $needle );
}
}

View File

@ -4,6 +4,7 @@ namespace Activitypub;
use WP_Error;
use WP_Comment_Query;
use Activitypub\Http;
use Activitypub\Webfinger;
use Activitypub\Activity\Activity;
use Activitypub\Collection\Followers;
use Activitypub\Collection\Users;
@ -168,6 +169,27 @@ function url_to_authorid( $url ) {
return 0;
}
/**
* Verify if url is a wp_ap_comment,
* Or if it is a previously received remote comment
*
* @return int comment_id
*/
function is_comment() {
$comment_id = get_query_var( 'c', null );
if ( ! is_null( $comment_id ) ) {
$comment = \get_comment( $comment_id );
// Only return local origin comments
if ( $comment && $comment->user_id ) {
return $comment_id;
}
}
return false;
}
/**
* Check for Tombstone Objects
*
@ -579,7 +601,7 @@ function get_active_users( $duration = 1 ) {
global $wpdb;
$query = "SELECT COUNT( DISTINCT post_author ) FROM {$wpdb->posts} WHERE post_type = 'post' AND post_status = 'publish' AND post_date <= DATE_SUB( NOW(), INTERVAL %d MONTH )";
$query = $wpdb->prepare( $query, $duration );
$count = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$count = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
set_transient( $transient_key, $count, DAY_IN_SECONDS );
}
@ -674,16 +696,33 @@ function url_to_commentid( $url ) {
return null;
}
// check for local comment
if ( \wp_parse_url( \site_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) {
$query = \wp_parse_url( $url, PHP_URL_QUERY );
if ( $query ) {
parse_str( $query, $params );
if ( ! empty( $params['c'] ) ) {
$comment = \get_comment( $params['c'] );
if ( $comment ) {
return $comment->comment_ID;
}
}
}
}
$args = array(
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'source_url',
'key' => 'source_url',
'value' => $url,
),
array(
'key' => 'source_id',
'key' => 'source_id',
'value' => $url,
),
),
@ -698,3 +737,40 @@ function url_to_commentid( $url ) {
return null;
}
/**
* Get the URI of an ActivityPub object
*
* @param array $object The ActivityPub object
*
* @return string The URI of the ActivityPub object
*/
function object_to_uri( $object ) {
// check if it is already simple
if ( ! $object || is_string( $object ) ) {
return $object;
}
// check if it is a list, then take first item
// this plugin does not support collections
if ( array_is_list( $object ) ) {
$object = $object[0];
}
// check if it is simplified now
if ( is_string( $object ) ) {
return $object;
}
// return part of Object that makes most sense
switch ( $object['type'] ) {
case 'Link':
$object = $object['href'];
break;
default:
$object = $object['id'];
break;
}
return $object;
}

View File

@ -15,7 +15,12 @@ class Create {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_create', array( self::class, 'handle_create' ), 10, 3 );
\add_action(
'activitypub_inbox_create',
array( self::class, 'handle_create' ),
10,
3
);
}
/**

View File

@ -15,11 +15,24 @@ class Delete {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_delete', array( self::class, 'handle_delete' ), 10, 2 );
\add_action(
'activitypub_inbox_delete',
array( self::class, 'handle_delete' )
);
// defer signature verification for `Delete` requests.
\add_filter( 'activitypub_defer_signature_verification', array( self::class, 'defer_signature_verification' ), 10, 2 );
\add_filter(
'activitypub_defer_signature_verification',
array( self::class, 'defer_signature_verification' ),
10,
2
);
// side effect
\add_action( 'activitypub_delete_actor_interactions', array( self::class, 'delete_interactions' ), 10, 1 );
\add_action(
'activitypub_delete_actor_interactions',
array( self::class, 'delete_interactions' )
);
}
/**
@ -28,7 +41,7 @@ class Delete {
* @param array $activity The delete activity.
* @param int $user_id The ID of the user performing the delete activity.
*/
public static function handle_delete( $activity, $user_id ) {
public static function handle_delete( $activity ) {
$object_type = isset( $activity['object']['type'] ) ? $activity['object']['type'] : '';
switch ( $object_type ) {
@ -39,7 +52,7 @@ class Delete {
case 'Organization':
case 'Service':
case 'Application':
self::maybe_delete_follower( $user_id, $activity );
self::maybe_delete_follower( $activity );
break;
// Object and Link Types
// @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types

View File

@ -14,8 +14,17 @@ class Follow {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_follow', array( self::class, 'handle_follow' ), 10, 2 );
\add_action( 'activitypub_followers_post_follow', array( self::class, 'send_follow_response' ), 10, 4 );
\add_action(
'activitypub_inbox_follow',
array( self::class, 'handle_follow' )
);
\add_action(
'activitypub_followers_post_follow',
array( self::class, 'send_follow_response' ),
10,
4
);
}
/**
@ -24,11 +33,30 @@ class Follow {
* @param array $activity The activity object
* @param int $user_id The user ID
*/
public static function handle_follow( $activity, $user_id ) {
// save follower
$follower = Followers::add_follower( $user_id, $activity['actor'] );
public static function handle_follow( $activity ) {
$user = Users::get_by_resource( $activity['object'] );
do_action( 'activitypub_followers_post_follow', $activity['actor'], $activity, $user_id, $follower );
if ( ! $user || is_wp_error( $user ) ) {
// If we can not find a user,
// we can not initiate a follow process
return;
}
$user_id = $user->get__id();
// save follower
$follower = Followers::add_follower(
$user_id,
$activity['actor']
);
do_action(
'activitypub_followers_post_follow',
$activity['actor'],
$activity,
$user_id,
$follower
);
}
/**

View File

@ -1,6 +1,7 @@
<?php
namespace Activitypub\Handler;
use Activitypub\Collection\Users;
use Activitypub\Collection\Followers;
/**
@ -11,7 +12,10 @@ class Undo {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_undo', array( self::class, 'handle_undo' ), 10, 2 );
\add_action(
'activitypub_inbox_undo',
array( self::class, 'handle_undo' )
);
}
/**
@ -20,11 +24,23 @@ class Undo {
* @param array $activity The JSON "Undo" Activity
* @param int $user_id The ID of the ID of the WordPress User
*/
public static function handle_undo( $activity, $user_id ) {
public static function handle_undo( $activity ) {
if (
isset( $activity['object']['type'] ) &&
'Follow' === $activity['object']['type']
'Follow' === $activity['object']['type'] &&
isset( $activity['object']['object'] ) &&
filter_var( $activity['object']['object'], FILTER_VALIDATE_URL )
) {
$user = Users::get_by_resource( $activity['object']['object'] );
if ( ! $user || is_wp_error( $user ) ) {
// If we can not find a user,
// we can not initiate a follow process
return;
}
$user_id = $user->get__id();
Followers::remove_follower( $user_id, $activity['actor'] );
}
}

View File

@ -14,7 +14,10 @@ class Update {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_action( 'activitypub_inbox_update', array( self::class, 'handle_update' ), 10, 2 );
\add_action(
'activitypub_inbox_update',
array( self::class, 'handle_update' )
);
}
/**
@ -23,7 +26,7 @@ class Update {
* @param array $array The activity-object
* @param int $user_id The id of the local blog-user
*/
public static function handle_update( $array, $user_id ) {
public static function handle_update( $array ) {
$object_type = isset( $array['object']['type'] ) ? $array['object']['type'] : '';
switch ( $object_type ) {
@ -45,7 +48,7 @@ class Update {
case 'Video':
case 'Event':
case 'Document':
self::update_interaction( $array, $user_id );
self::update_interaction( $array );
break;
// Minimal Activity
// @see https://www.w3.org/TR/activitystreams-core/#example-1
@ -62,15 +65,18 @@ class Update {
*
* @return void
*/
public static function update_interaction( $activity, $user_id ) {
$state = Interactions::update_comment( $activity );
$reaction = null;
public static function update_interaction( $activity ) {
$commentdata = Interactions::update_comment( $activity );
$reaction = null;
if ( $state && ! \is_wp_error( $reaction ) ) {
$reaction = \get_comment( $state );
if ( ! empty( $commentdata['comment_ID'] ) ) {
$state = 1;
$reaction = \get_comment( $commentdata['comment_ID'] );
} else {
$state = $commentdata;
}
\do_action( 'activitypub_handled_update', $activity, $user_id, $state, $reaction );
\do_action( 'activitypub_handled_update', $activity, null, $state, $reaction );
}
/**

View File

@ -38,6 +38,15 @@ class Application_User extends Blog_User {
return get_rest_url_by_path( 'application' );
}
/**
* Returns the User-URL with @-Prefix for the username.
*
* @return string The User-URL with @-Prefix for the username.
*/
public function get_alternate_url() {
return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
}
public function get_name() {
return 'application';
}
@ -69,4 +78,8 @@ class Application_User extends Blog_User {
public function get_indexable() {
return false;
}
public function get_type() {
return $this->type;
}
}

View File

@ -100,12 +100,12 @@ class Blog_User extends User {
}
/**
* Returns the User-URL with @-Prefix for the username.
* Get blog's homepage URL.
*
* @return string The User-URL with @-Prefix for the username.
* @return string The User-Url.
*/
public function get_at_url() {
return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
public function get_alternate_url() {
return \esc_url( \trailingslashit( get_home_url() ) );
}
/**

View File

@ -1,6 +1,7 @@
<?php
namespace Activitypub\Model;
use Activitypub\Collection\Users;
use Activitypub\Transformer\Post as Post_Transformer;
/**
@ -61,7 +62,7 @@ class Post {
* @return string The URL of the Actor.
*/
public function get_actor() {
$user = User_Factory::get_by_id( $this->get_user_id() );
$user = Users::get_by_id( $this->get_user_id() );
return $user->get_url();
}

View File

@ -64,7 +64,7 @@ class User extends Actor {
*
* @var string<url>
*/
protected $resource;
protected $webfinger;
/**
* Restrict posting to mods
@ -135,8 +135,8 @@ class User extends Actor {
*
* @return string The User-URL with @-Prefix for the username.
*/
public function get_at_url() {
return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_username() );
public function get_alternate_url() {
return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
}
public function get_preferred_username() {
@ -226,6 +226,18 @@ class User extends Actor {
return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) );
}
public function get_endpoints() {
$endpoints = null;
if ( ACTIVITYPUB_SHARED_INBOX_FEATURE ) {
$endpoints = array(
'sharedInbox' => get_rest_url_by_path( 'inbox' ),
);
}
return $endpoints;
}
/**
* Extend the User-Output with Attachments.
*
@ -274,10 +286,14 @@ class User extends Actor {
*
* @return string The Webfinger-Identifier.
*/
public function get_resource() {
public function get_webfinger() {
return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
}
public function get_resource() {
return $this->get_webfinger();
}
public function get_canonical_url() {
return $this->get_url();
}

View File

@ -1,34 +0,0 @@
<?php
namespace Activitypub\Peer;
/**
* ActivityPub Followers DB-Class
*
* @author Matthias Pfefferle
*/
class Followers {
public static function get_followers( $author_id ) {
_deprecated_function( __METHOD__, '1.0.0', '\Activitypub\Collection\Followers::get_followers' );
return \Activitypub\Collection\Followers::get_followers( $author_id );
}
public static function count_followers( $author_id ) {
_deprecated_function( __METHOD__, '1.0.0', '\Activitypub\Collection\Followers::count_followers' );
return \Activitypub\Collection\Followers::count_followers( $author_id );
}
public static function add_follower( $actor, $author_id ) {
_deprecated_function( __METHOD__, '1.0.0', '\Activitypub\Collection\Followers::add_follower' );
return \Activitypub\Collection\Followers::add_follower( $author_id, $actor );
}
public static function remove_follower( $actor, $author_id ) {
_deprecated_function( __METHOD__, '1.0.0', '\Activitypub\Collection\Followers::remove_follower' );
return \Activitypub\Collection\Followers::remove_follower( $author_id, $actor );
}
}

View File

@ -94,7 +94,7 @@ class Followers {
// phpcs:ignore
$json->orderedItems = array_map(
function( $item ) use ( $context ) {
function ( $item ) use ( $context ) {
if ( 'full' === $context ) {
return $item->to_array();
}

View File

@ -8,6 +8,7 @@ use Activitypub\Activity\Activity;
use Activitypub\Collection\Users as User_Collection;
use function Activitypub\get_context;
use function Activitypub\object_to_uri;
use function Activitypub\url_to_authorid;
use function Activitypub\get_rest_url_by_path;
use function Activitypub\get_remote_metadata_by_actor;
@ -93,11 +94,8 @@ class Inbox {
$json->generator = 'http://wordpress.org/?v=' . \get_bloginfo_rss( 'version' );
$json->type = 'OrderedCollectionPage';
$json->partOf = get_rest_url_by_path( sprintf( 'users/%d/inbox', $user->get__id() ) ); // phpcs:ignore
$json->totalItems = 0; // phpcs:ignore
$json->orderedItems = array(); // phpcs:ignore
$json->first = $json->partOf; // phpcs:ignore
// filter output
@ -154,37 +152,10 @@ class Inbox {
$data = $request->get_json_params();
$activity = Activity::init_from_array( $data );
$type = $request->get_param( 'type' );
$users = self::get_recipients( $data );
$type = \strtolower( $type );
if ( ! $users ) {
return new WP_Error(
'rest_invalid_param',
\__( 'No recipients found', 'activitypub' ),
array(
'status' => 400,
'params' => array(
'to' => \__( 'Please check/validate "to" field', 'activitypub' ),
'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ),
'cc' => \__( 'Please check/validate "cc" field', 'activitypub' ),
'bcc' => \__( 'Please check/validate "bcc" field', 'activitypub' ),
'audience' => \__( 'Please check/validate "audience" field', 'activitypub' ),
),
)
);
}
foreach ( $users as $user ) {
$user = User_Collection::get_by_various( $user );
if ( is_wp_error( $user ) ) {
continue;
}
$type = \strtolower( $type );
\do_action( 'activitypub_inbox', $data, $user->ID, $type, $activity );
\do_action( "activitypub_inbox_{$type}", $data, $user->ID, $activity );
}
\do_action( 'activitypub_inbox', $data, null, $type, $activity );
\do_action( "activitypub_inbox_{$type}", $data, null, $activity );
$rest_response = new WP_REST_Response( array(), 202 );
$rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
@ -236,15 +207,8 @@ class Inbox {
$params['actor'] = array(
'required' => true,
'sanitize_callback' => function( $param, $request, $key ) {
if ( \is_array( $param ) ) {
if ( isset( $param['id'] ) ) {
$param = $param['id'];
} else {
$param = $param['url'];
}
}
return \esc_url_raw( $param );
'sanitize_callback' => function ( $param, $request, $key ) {
return object_to_uri( $param );
},
);
@ -252,7 +216,7 @@ class Inbox {
'required' => true,
//'type' => 'enum',
//'enum' => array( 'Create' ),
//'sanitize_callback' => function( $param, $request, $key ) {
//'sanitize_callback' => function ( $param, $request, $key ) {
// return \strtolower( $param );
//},
);
@ -285,11 +249,8 @@ class Inbox {
$params['actor'] = array(
'required' => true,
//'type' => array( 'object', 'string' ),
'sanitize_callback' => function( $param, $request, $key ) {
if ( ! \is_string( $param ) ) {
$param = $param['id'];
}
return \esc_url_raw( $param );
'sanitize_callback' => function ( $param, $request, $key ) {
return object_to_uri( $param );
},
);
@ -297,7 +258,7 @@ class Inbox {
'required' => true,
//'type' => 'enum',
//'enum' => array( 'Create' ),
//'sanitize_callback' => function( $param, $request, $key ) {
//'sanitize_callback' => function ( $param, $request, $key ) {
// return \strtolower( $param );
//},
);
@ -309,7 +270,7 @@ class Inbox {
$params['to'] = array(
'required' => false,
'sanitize_callback' => function( $param, $request, $key ) {
'sanitize_callback' => function ( $param, $request, $key ) {
if ( \is_string( $param ) ) {
$param = array( $param );
}
@ -319,7 +280,7 @@ class Inbox {
);
$params['cc'] = array(
'sanitize_callback' => function( $param, $request, $key ) {
'sanitize_callback' => function ( $param, $request, $key ) {
if ( \is_string( $param ) ) {
$param = array( $param );
}
@ -329,7 +290,7 @@ class Inbox {
);
$params['bcc'] = array(
'sanitize_callback' => function( $param, $request, $key ) {
'sanitize_callback' => function ( $param, $request, $key ) {
if ( \is_string( $param ) ) {
$param = array( $param );
}

View File

@ -105,6 +105,12 @@ class Nodeinfo {
'outbound' => array(),
);
$nodeinfo['metadata'] = array(
'nodeName' => \get_bloginfo( 'name' ),
'nodeDescription' => \get_bloginfo( 'description' ),
'nodeIcon' => \get_site_icon_url(),
);
return new WP_REST_Response( $nodeinfo, 200 );
}
@ -169,6 +175,10 @@ class Nodeinfo {
'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0',
'href' => get_rest_url_by_path( 'nodeinfo' ),
),
array(
'rel' => 'https://www.w3.org/ns/activitystreams#Application',
'href' => get_rest_url_by_path( 'application' ),
),
);
return new \WP_REST_Response( $discovery, 200 );

View File

@ -50,14 +50,13 @@ class Users {
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'remote_follow_get' ),
'permission_callback' => '__return_true',
'args' => array(
'resource' => array(
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
),
'permission_callback' => '__return_true',
),
)
);
@ -124,7 +123,7 @@ class Users {
return $template;
}
$resource = $user->get_resource();
$resource = $user->get_webfinger();
$url = str_replace( '{uri}', $resource, $template );
return new WP_REST_Response(

View File

@ -72,7 +72,7 @@ class Webfinger {
$params['resource'] = array(
'required' => true,
'type' => 'string',
'pattern' => '^acct:(.+)@(.+)$',
'pattern' => '^(acct:)|^(https?://)(.+)$',
);
return $params;
@ -94,10 +94,13 @@ class Webfinger {
$aliases = array(
$user->get_url(),
$user->get_alternate_url(),
);
$aliases = array_unique( $aliases );
$profile = array(
'subject' => $resource,
'subject' => sprintf( 'acct:%s', $user->get_webfinger() ),
'aliases' => array_values( array_unique( $aliases ) ),
'links' => array(
array(
@ -113,9 +116,9 @@ class Webfinger {
),
);
if ( 'Group' === $user->get_type() ) {
if ( 'Person' !== $user->get_type() ) {
$profile['links'][0]['properties'] = array(
'https://www.w3.org/ns/activitystreams#type' => 'Group',
'https://www.w3.org/ns/activitystreams#type' => $user->get_type(),
);
}

View File

@ -5,6 +5,8 @@ use WP_List_Table;
use Activitypub\Collection\Users;
use Activitypub\Collection\Followers as FollowerCollection;
use function Activitypub\object_to_uri;
if ( ! \class_exists( '\WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
@ -101,7 +103,7 @@ class Followers extends WP_List_Table {
'icon' => esc_attr( $follower->get_icon_url() ),
'post_title' => esc_attr( $follower->get_name() ),
'username' => esc_attr( $follower->get_preferred_username() ),
'url' => esc_attr( $follower->get_url() ),
'url' => esc_attr( object_to_uri( $follower->get_url() ) ),
'identifier' => esc_attr( $follower->get_id() ),
'published' => esc_attr( $follower->get_published() ),
'modified' => esc_attr( $follower->get_updated() ),

View File

@ -0,0 +1,49 @@
<?php
namespace Activitypub\Transformer;
use Activitypub\Transformer\Post;
/**
* WordPress Attachment Transformer
*
* The Attachment Transformer is responsible for transforming a WP_Post object into different other
* Object-Types.
*
* Currently supported are:
*
* - Activitypub\Activity\Base_Object
*/
class Attachment extends Post {
/**
* Generates all Media Attachments for a Post.
*
* @return array The Attachments.
*/
protected function get_attachment() {
$mime_type = get_post_mime_type( $this->wp_object->ID );
$media_type = preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type );
switch ( $media_type ) {
case 'audio':
case 'video':
$type = 'Document';
break;
case 'image':
$type = 'Image';
break;
}
$attachment = array(
'type' => $type,
'url' => wp_get_attachment_url( $this->wp_object->ID ),
'mediaType' => $mime_type,
);
$alt = \get_post_meta( $this->wp_object->ID, '_wp_attachment_image_alt', true );
if ( $alt ) {
$attachment['name'] = $alt;
}
return $attachment;
}
}

View File

@ -0,0 +1,110 @@
<?php
namespace Activitypub\Transformer;
use WP_Post;
use WP_Comment;
use Activitypub\Activity\Activity;
use Activitypub\Activity\Base_Object;
/**
* WordPress Base Transformer
*
* Transformers are responsible for transforming a WordPress objects into different ActivityPub
* Object-Types or Activities.
*/
abstract class Base {
/**
* The WP_Post or WP_Comment object.
*
* This is the source object of the transformer.
*
* @var WP_Post|WP_Comment
*/
protected $wp_object;
/**
* Static function to Transform a WordPress Object.
*
* This helps to chain the output of the Transformer.
*
* @param WP_Post|WP_Comment $wp_object The WordPress object
*
* @return void
*/
public static function transform( $object ) {
return new static( $object );
}
/**
* Base constructor.
*
* @param WP_Post|WP_Comment $wp_object The WordPress object
*/
public function __construct( $wp_object ) {
$this->wp_object = $wp_object;
}
/**
* Transform the WordPress Object into an ActivityPub Object.
*
* @return Activitypub\Activity\Base_Object
*/
public function to_object() {
$activitypub_object = new Base_Object();
$vars = $activitypub_object->get_object_var_keys();
foreach ( $vars as $var ) {
$getter = 'get_' . $var;
if ( method_exists( $this, $getter ) ) {
$value = call_user_func( array( $this, $getter ) );
if ( isset( $value ) ) {
$setter = 'set_' . $var;
call_user_func( array( $activitypub_object, $setter ), $value );
}
}
}
return $activitypub_object;
}
/**
* Transforms the ActivityPub Object to an Activity
*
* @param string $type The Activity-Type.
*
* @return \Activitypub\Activity\Activity The Activity.
*/
public function to_activity( $type ) {
$object = $this->to_object();
$activity = new Activity();
$activity->set_type( $type );
$activity->set_object( $object );
// Use simple Object (only ID-URI) for Like and Announce
if ( in_array( $type, array( 'Like', 'Announce' ), true ) ) {
$activity->set_object( $object->get_id() );
}
return $activity;
}
/**
* Returns the ID of the WordPress Object.
*
* @return int The ID of the WordPress Object
*/
abstract public function get_wp_user_id();
/**
* Change the User-ID of the WordPress Post.
*
* @return int The User-ID of the WordPress Post
*/
abstract public function change_wp_user_id( $user_id );
}

View File

@ -0,0 +1,274 @@
<?php
namespace Activitypub\Transformer;
use WP_Comment;
use WP_Comment_Query;
use Activitypub\Model\Blog_User;
use Activitypub\Collection\Users;
use Activitypub\Transformer\Base;
use function Activitypub\is_single_user;
use function Activitypub\get_rest_url_by_path;
/**
* WordPress Comment Transformer
*
* The Comment Transformer is responsible for transforming a WP_Comment object into different
* Object-Types.
*
* Currently supported are:
*
* - Activitypub\Activity\Base_Object
*/
class Comment extends Base {
/**
* Returns the User-ID of the WordPress Comment.
*
* @return int The User-ID of the WordPress Comment
*/
public function get_wp_user_id() {
return $this->wp_object->user_id;
}
/**
* Change the User-ID of the WordPress Comment.
*
* @return int The User-ID of the WordPress Comment
*/
public function change_wp_user_id( $user_id ) {
$this->wp_object->user_id = $user_id;
}
/**
* Transforms the WP_Comment object to an ActivityPub Object
*
* @see \Activitypub\Activity\Base_Object
*
* @return \Activitypub\Activity\Base_Object The ActivityPub Object
*/
public function to_object() {
$comment = $this->wp_object;
$object = parent::to_object();
$object->set_url( \get_comment_link( $comment->comment_ID ) );
$object->set_type( 'Note' );
$published = \strtotime( $comment->comment_date_gmt );
$object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
$updated = \get_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', true );
if ( $updated > $published ) {
$object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) );
}
$object->set_content_map(
array(
$this->get_locale() => $this->get_content(),
)
);
$path = sprintf( 'users/%d/followers', intval( $comment->comment_author ) );
$object->set_to(
array(
'https://www.w3.org/ns/activitystreams#Public',
get_rest_url_by_path( $path ),
)
);
return $object;
}
/**
* Returns the User-URL of the Author of the Post.
*
* If `single_user` mode is enabled, the URL of the Blog-User is returned.
*
* @return string The User-URL.
*/
protected function get_attributed_to() {
if ( is_single_user() ) {
$user = new Blog_User();
return $user->get_url();
}
return Users::get_by_id( $this->wp_object->user_id )->get_url();
}
/**
* Returns the content for the ActivityPub Item.
*
* The content will be generated based on the user settings.
*
* @return string The content.
*/
protected function get_content() {
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$comment = $this->wp_object;
$content = $comment->comment_content;
$content = \wpautop( $content );
$content = \preg_replace( '/[\n\r\t]/', '', $content );
$content = \trim( $content );
$content = \apply_filters( 'the_content', $content, $comment );
return $content;
}
/**
* Returns the in-reply-to for the ActivityPub Item.
*
* @return int The URL of the in-reply-to.
*/
protected function get_in_reply_to() {
$comment = $this->wp_object;
$parent_comment = \get_comment( $comment->comment_parent );
if ( $parent_comment ) {
$comment_meta = \get_comment_meta( $parent_comment->comment_ID );
if ( ! empty( $comment_meta['source_id'][0] ) ) {
$in_reply_to = $comment_meta['source_id'][0];
} elseif ( ! empty( $comment_meta['source_url'][0] ) ) {
$in_reply_to = $comment_meta['source_url'][0];
} else {
$in_reply_to = $this->generate_id( $parent_comment );
}
} else {
$in_reply_to = \get_permalink( $comment->comment_post_ID );
}
return $in_reply_to;
}
/**
* Returns the ID of the ActivityPub Object.
*
* @see https://www.w3.org/TR/activitypub/#obj-id
* @see https://github.com/tootsuite/mastodon/issues/13879
*
* @return string ActivityPub URI for comment
*/
protected function get_id() {
$comment = $this->wp_object;
return $this->generate_id( $comment );
}
/**
* Generates an ActivityPub URI for a comment
*
* @param WP_Comment|int $comment A comment object or comment ID
*
* @return string ActivityPub URI for comment
*/
protected function generate_id( $comment ) {
$comment = get_comment( $comment );
return \add_query_arg(
array(
'c' => $comment->comment_ID,
),
\trailingslashit( site_url() )
);
}
/**
* Returns a list of Mentions, used in the Comment.
*
* @see https://docs.joinmastodon.org/spec/activitypub/#Mention
*
* @return array The list of Mentions.
*/
protected function get_cc() {
$cc = array();
$mentions = $this->get_mentions();
if ( $mentions ) {
foreach ( $mentions as $mention => $url ) {
$cc[] = $url;
}
}
$comment_query = new WP_Comment_Query(
array(
'post_id' => $this->wp_object->comment_post_ID,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
array(
'key' => 'source_id',
'compare' => 'EXISTS',
),
),
)
);
if ( $comment_query->comments ) {
foreach ( $comment_query->comments as $comment ) {
if ( empty( $comment->comment_author_url ) ) {
continue;
}
$cc[] = \esc_url( $comment->comment_author_url );
}
}
$cc = \array_unique( $cc );
return $cc;
}
/**
* Returns a list of Tags, used in the Comment.
*
* This includes Hash-Tags and Mentions.
*
* @return array The list of Tags.
*/
protected function get_tag() {
$tags = array();
$mentions = $this->get_mentions();
if ( $mentions ) {
foreach ( $mentions as $mention => $url ) {
$tag = array(
'type' => 'Mention',
'href' => \esc_url( $url ),
'name' => \esc_html( $mention ),
);
$tags[] = $tag;
}
}
return \array_unique( $tags, SORT_REGULAR );
}
/**
* Helper function to get the @-Mentions from the comment content.
*
* @return array The list of @-Mentions.
*/
protected function get_mentions() {
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->comment_content, $this->wp_object );
}
/**
* Returns the locale of the post.
*
* @return string The locale of the post.
*/
public function get_locale() {
$comment_id = $this->wp_object->ID;
$lang = \strtolower( \strtok( \get_locale(), '_-' ) );
/**
* Filter the locale of the comment.
*
* @param string $lang The locale of the comment.
* @param int $comment_id The comment ID.
* @param WP_Post $post The comment object.
*
* @return string The filtered locale of the comment.
*/
return apply_filters( 'activitypub_comment_locale', $lang, $comment_id, $this->wp_object );
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Activitypub\Transformer;
use Activitypub\Transformer\Post;
use Activitypub\Transformer\Comment;
use Activitypub\Transformer\Attachment;
/**
* Transformer Factory
*/
class Factory {
public static function get_transformer( $object ) {
/**
* Filter the transformer for a given object.
*
* Add your own transformer based on the object class or the object type.
*
* Example usage:
*
* // Filter be object class
* add_filter( 'activitypub_transformer', function( $transformer, $object, $object_class ) {
* if ( $object_class === 'WP_Post' ) {
* return new My_Post_Transformer( $object );
* }
* return $transformer;
* }, 10, 3 );
*
* // Filter be object type
* add_filter( 'activitypub_transformer', function( $transformer, $object, $object_class ) {
* if ( $object->post_type === 'event' ) {
* return new My_Event_Transformer( $object );
* }
* return $transformer;
* }, 10, 3 );
*
* @param Activitypub\Transformer\Base $transformer The transformer to use.
* @param mixed $object The object to transform.
* @param string $object_class The class of the object to transform.
*
* @return mixed The transformer to use.
*/
$transformer = apply_filters( 'activitypub_transformer', null, $object, get_class( $object ) );
if ( $transformer ) {
return $transformer;
}
// use default transformer
switch ( get_class( $object ) ) {
case 'WP_Post':
if ( 'attachment' === $object->post_type ) {
return new Attachment( $object );
}
return new Post( $object );
case 'WP_Comment':
return new Comment( $object );
default:
return null;
}
}
}

View File

@ -2,10 +2,11 @@
namespace Activitypub\Transformer;
use WP_Post;
use Activitypub\Collection\Users;
use Activitypub\Model\Blog_User;
use Activitypub\Activity\Base_Object;
use Activitypub\Shortcodes;
use Activitypub\Model\Blog_User;
use Activitypub\Transformer\Base;
use Activitypub\Collection\Users;
use Activitypub\Activity\Base_Object;
use function Activitypub\esc_hashtag;
use function Activitypub\is_single_user;
@ -15,42 +16,32 @@ use function Activitypub\site_supports_blocks;
/**
* WordPress Post Transformer
*
* The Post Transformer is responsible for transforming a WP_Post object into different othe
* The Post Transformer is responsible for transforming a WP_Post object into different other
* Object-Types.
*
* Currently supported are:
*
* - Activitypub\Activity\Base_Object
*/
class Post {
class Post extends Base {
/**
* The WP_Post object.
* Returns the ID of the WordPress Post.
*
* @var WP_Post
* @return int The ID of the WordPress Post
*/
protected $wp_post;
/**
* Static function to Transform a WP_Post Object.
*
* This helps to chain the output of the Transformer.
*
* @param WP_Post $wp_post The WP_Post object
*
* @return void
*/
public static function transform( WP_Post $wp_post ) {
return new static( $wp_post );
public function get_wp_user_id() {
return $this->wp_object->post_author;
}
/**
* Change the User-ID of the WordPress Post.
*
*
* @param WP_Post $wp_post
* @return int The User-ID of the WordPress Post
*/
public function __construct( WP_Post $wp_post ) {
$this->wp_post = $wp_post;
public function change_wp_user_id( $user_id ) {
$this->wp_object->post_author = $user_id;
return $this;
}
/**
@ -61,31 +52,25 @@ class Post {
* @return \Activitypub\Activity\Base_Object The ActivityPub Object
*/
public function to_object() {
$wp_post = $this->wp_post;
$object = new Base_Object();
$post = $this->wp_object;
$object = parent::to_object();
$object->set_id( $this->get_id() );
$object->set_url( $this->get_url() );
$object->set_type( $this->get_object_type() );
$published = \strtotime( $wp_post->post_date_gmt );
$published = \strtotime( $post->post_date_gmt );
$object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
$updated = \strtotime( $wp_post->post_modified_gmt );
$updated = \strtotime( $post->post_modified_gmt );
if ( $updated > $published ) {
$object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) );
}
$object->set_attributed_to( $this->get_attributed_to() );
$object->set_content( $this->get_content() );
$object->set_content_map(
array(
$this->get_locale() => $this->get_content(),
)
);
$path = sprintf( 'users/%d/followers', intval( $wp_post->post_author ) );
$path = sprintf( 'users/%d/followers', intval( $post->post_author ) );
$object->set_to(
array(
@ -93,9 +78,6 @@ class Post {
get_rest_url_by_path( $path ),
)
);
$object->set_cc( $this->get_cc() );
$object->set_attachment( $this->get_attachments() );
$object->set_tag( $this->get_tags() );
return $object;
}
@ -115,7 +97,7 @@ class Post {
* @return string The Posts URL.
*/
public function get_url() {
$post = $this->wp_post;
$post = $this->wp_object;
if ( 'trash' === get_post_status( $post ) ) {
$permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true );
@ -134,12 +116,19 @@ class Post {
* @return string The User-URL.
*/
protected function get_attributed_to() {
$blog_user = new Blog_User();
if ( is_single_user() ) {
$user = new Blog_User();
return $blog_user->get_url();
}
$user = Users::get_by_id( $this->wp_object->post_author );
if ( $user && ! is_wp_error( $user ) ) {
return $user->get_url();
}
return Users::get_by_id( $this->wp_post->post_author )->get_url();
return $blog_user->get_url();
}
/**
@ -147,12 +136,12 @@ class Post {
*
* @return array The Attachments.
*/
protected function get_attachments() {
protected function get_attachment() {
// Once upon a time we only supported images, but we now support audio/video as well.
// We maintain the image-centric naming for backwards compatibility.
$max_media = intval( \apply_filters( 'activitypub_max_image_attachments', \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ) );
if ( site_supports_blocks() && \has_blocks( $this->wp_post->post_content ) ) {
if ( site_supports_blocks() && \has_blocks( $this->wp_object->post_content ) ) {
return $this->get_block_attachments( $max_media );
}
@ -172,7 +161,7 @@ class Post {
return array();
}
$id = $this->wp_post->ID;
$id = $this->wp_object->ID;
$media_ids = array();
@ -182,7 +171,7 @@ class Post {
}
if ( $max_media > 0 ) {
$blocks = \parse_blocks( $this->wp_post->post_content );
$blocks = \parse_blocks( $this->wp_object->post_content );
$media_ids = self::get_media_ids_from_blocks( $blocks, $media_ids, $max_media );
}
@ -191,6 +180,103 @@ class Post {
/**
* Get image attachments from the classic editor.
* This is imperfect as the contained images aren't necessarily the
* same as the attachments.
*
* @param int $max_images The maximum number of images to return.
*
* @return array The attachment IDs.
*/
protected function get_classic_editor_image_attachments( $max_images ) {
// max images can't be negative or zero
if ( $max_images <= 0 ) {
return array();
}
$image_ids = array();
$query = new \WP_Query(
array(
'post_parent' => $this->wp_object->ID,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => 'ASC',
'orderby' => 'menu_order ID',
'posts_per_page' => $max_images,
)
);
foreach ( $query->get_posts() as $attachment ) {
if ( ! \in_array( $attachment->ID, $image_ids, true ) ) {
$image_ids[] = $attachment->ID;
}
}
return $image_ids;
}
/**
* Get image embeds from the classic editor by parsing HTML.
*
* @param int $max_images The maximum number of images to return.
*
* @return array The attachment IDs.
*/
protected function get_classic_editor_image_embeds( $max_images ) {
// if someone calls that function directly, bail
if ( ! \class_exists( '\WP_HTML_Tag_Processor' ) ) {
return array();
}
// max images can't be negative or zero
if ( $max_images <= 0 ) {
return array();
}
$image_ids = array();
$base = \wp_get_upload_dir()['baseurl'];
$content = \get_post_field( 'post_content', $this->wp_object );
$tags = new \WP_HTML_Tag_Processor( $content );
// This linter warning is a false positive - we have to
// re-count each time here as we modify $image_ids.
// phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
while ( $tags->next_tag( 'img' ) && ( \count( $image_ids ) < $max_images ) ) {
$src = $tags->get_attribute( 'src' );
// If the img source is in our uploads dir, get the
// associated ID. Note: if there's a -500x500
// type suffix, we remove it, but we try the original
// first in case the original image is actually called
// that. Likewise, we try adding the -scaled suffix for
// the case that this is a small version of an image
// that was big enough to get scaled down on upload:
// https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
if ( null !== $src && \str_starts_with( $src, $base ) ) {
$img_id = \attachment_url_to_postid( $src );
if ( 0 === $img_id ) {
$count = 0;
$src = preg_replace( '/-(?:\d+x\d+)(\.[a-zA-Z]+)$/', '$1', $src, 1, $count );
if ( $count > 0 ) {
$img_id = \attachment_url_to_postid( $src );
}
}
if ( 0 === $img_id ) {
$src = preg_replace( '/(\.[a-zA-Z]+)$/', '-scaled$1', $src );
$img_id = \attachment_url_to_postid( $src );
}
if ( 0 !== $img_id ) {
if ( ! \in_array( $img_id, $image_ids, true ) ) {
$image_ids[] = $img_id;
}
}
}
}
return $image_ids;
}
/**
* Get post images from the classic editor.
* Note that audio/video attachments are only supported in the block editor.
*
* @param int $max_images The maximum number of images to return.
@ -203,35 +289,24 @@ class Post {
return array();
}
$id = $this->wp_post->ID;
$id = $this->wp_object->ID;
$image_ids = array();
// list post thumbnail first if this post has one
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
$image_ids[] = \get_post_thumbnail_id( $id );
--$max_images;
}
if ( $max_images > 0 ) {
$query = new \WP_Query(
array(
'post_parent' => $id,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => 'ASC',
'orderby' => 'menu_order ID',
'posts_per_page' => $max_images,
)
);
foreach ( $query->get_posts() as $attachment ) {
if ( ! \in_array( $attachment->ID, $image_ids, true ) ) {
$image_ids[] = $attachment->ID;
}
if ( \count( $image_ids ) < $max_images ) {
if ( \class_exists( '\WP_HTML_Tag_Processor' ) ) {
$image_ids = \array_merge( $image_ids, $this->get_classic_editor_image_embeds( $max_images ) );
} else {
$image_ids = \array_merge( $image_ids, $this->get_classic_editor_image_attachments( $max_images ) );
}
}
$image_ids = \array_unique( $image_ids );
// unique then slice as the thumbnail may duplicate another image
$image_ids = \array_slice( \array_unique( $image_ids ), 0, $max_images );
return \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $image_ids ) );
}
@ -316,7 +391,7 @@ class Post {
*/
$thumbnail = apply_filters(
'activitypub_get_image',
self::get_image( $id, $image_size ),
self::get_wordpress_attachment( $id, $image_size ),
$id,
$image_size
);
@ -365,7 +440,7 @@ class Post {
*
* @return array|false Array of image data, or boolean false if no image is available.
*/
protected static function get_image( $id, $image_size = 'full' ) {
protected static function get_wordpress_attachment( $id, $image_size = 'full' ) {
/**
* Hook into the image retrieval process. Before image retrieval.
*
@ -395,17 +470,17 @@ class Post {
*
* @return string The Object-Type.
*/
protected function get_object_type() {
protected function get_type() {
if ( 'wordpress-post-format' !== \get_option( 'activitypub_object_type', 'note' ) ) {
return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) );
}
// Default to Article.
$object_type = 'Article';
$post_type = \get_post_type( $this->wp_post );
$post_type = \get_post_type( $this->wp_object );
switch ( $post_type ) {
case 'post':
$post_format = \get_post_format( $this->wp_post );
$post_format = \get_post_format( $this->wp_object );
switch ( $post_format ) {
case 'aside':
case 'status':
@ -481,10 +556,10 @@ class Post {
*
* @return array The list of Tags.
*/
protected function get_tags() {
protected function get_tag() {
$tags = array();
$post_tags = \get_the_tags( $this->wp_post->ID );
$post_tags = \get_the_tags( $this->wp_object->ID );
if ( $post_tags ) {
foreach ( $post_tags as $post_tag ) {
$tag = array(
@ -531,7 +606,7 @@ class Post {
do_action( 'activitypub_before_get_content', $post );
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$post = $this->wp_post;
$post = $this->wp_object;
$content = $this->get_post_content_template();
// Register our shortcodes just in time.
@ -559,19 +634,24 @@ class Post {
* @return string The Template.
*/
protected function get_post_content_template() {
if ( 'excerpt' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
return "[ap_excerpt]\n\n[ap_permalink type=\"html\"]";
$type = \get_option( 'activitypub_post_content_type', 'content' );
switch ( $type ) {
case 'excerpt':
$template = "[ap_excerpt]\n\n[ap_permalink type=\"html\"]";
break;
case 'title':
$template = "[ap_title]\n\n[ap_permalink type=\"html\"]";
break;
case 'content':
$template = "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]";
break;
default:
$template = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
break;
}
if ( 'title' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
return "[ap_title]\n\n[ap_permalink type=\"html\"]";
}
if ( 'content' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
return "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]";
}
return \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
return apply_filters( 'activitypub_object_content_template', $template, $this->wp_object );
}
/**
@ -580,7 +660,7 @@ class Post {
* @return array The list of @-Mentions.
*/
protected function get_mentions() {
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_post->post_content, $this->wp_post );
return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->post_content, $this->wp_object );
}
/**
@ -589,7 +669,7 @@ class Post {
* @return string The locale of the post.
*/
public function get_locale() {
$post_id = $this->wp_post->ID;
$post_id = $this->wp_object->ID;
$lang = \strtolower( \strtok( \get_locale(), '_-' ) );
/**
@ -601,6 +681,6 @@ class Post {
*
* @return string The filtered locale of the post.
*/
return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_post );
return apply_filters( 'activitypub_post_locale', $lang, $post_id, $this->wp_object );
}
}

View File

@ -3,6 +3,7 @@ namespace Activitypub\Integration;
use function Activitypub\get_total_users;
use function Activitypub\get_active_users;
use function Activitypub\get_rest_url_by_path;
/**
* Compatibility with the NodeInfo plugin
@ -14,8 +15,10 @@ class Nodeinfo {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_filter( 'nodeinfo_data', array( self::class, 'add_nodeinfo_discovery' ), 10, 2 );
\add_filter( 'nodeinfo2_data', array( self::class, 'add_nodeinfo2_discovery' ), 10 );
\add_filter( 'nodeinfo_data', array( self::class, 'add_nodeinfo_data' ), 10, 2 );
\add_filter( 'nodeinfo2_data', array( self::class, 'add_nodeinfo2_data' ), 10 );
\add_filter( 'wellknown_nodeinfo_data', array( self::class, 'add_wellknown_nodeinfo_data' ), 10, 2 );
}
/**
@ -26,7 +29,7 @@ class Nodeinfo {
*
* @return array The extended array
*/
public static function add_nodeinfo_discovery( $nodeinfo, $version ) {
public static function add_nodeinfo_data( $nodeinfo, $version ) {
if ( $version >= '2.0' ) {
$nodeinfo['protocols'][] = 'activitypub';
} else {
@ -50,7 +53,7 @@ class Nodeinfo {
*
* @return array The extended array
*/
public static function add_nodeinfo2_discovery( $nodeinfo ) {
public static function add_nodeinfo2_data( $nodeinfo ) {
$nodeinfo['protocols'][] = 'activitypub';
$nodeinfo['usage']['users'] = array(
@ -61,4 +64,20 @@ class Nodeinfo {
return $nodeinfo;
}
/**
* Extend the well-known nodeinfo data
*
* @param array $data The well-known nodeinfo data
*
* @return array The extended array
*/
public static function add_wellknown_nodeinfo_data( $data ) {
$data['links'][] = array(
'rel' => 'https://www.w3.org/ns/activitystreams#Application',
'href' => get_rest_url_by_path( 'application' ),
);
return $data;
}
}

View File

@ -14,8 +14,8 @@ class Webfinger {
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_filter( 'webfinger_user_data', array( self::class, 'add_user_discovery' ), 10, 3 );
\add_filter( 'webfinger_data', array( self::class, 'add_pseudo_user_discovery' ), 99, 2 );
\add_filter( 'webfinger_user_data', array( self::class, 'add_user_discovery' ), 1, 3 );
\add_filter( 'webfinger_data', array( self::class, 'add_pseudo_user_discovery' ), 1, 2 );
}
/**
@ -34,6 +34,11 @@ class Webfinger {
return $array;
}
$array['subject'] = sprintf( 'acct:%s', $user->get_webfinger() );
$array['aliases'][] = $user->get_url();
$array['aliases'][] = $user->get_alternate_url();
$array['links'][] = array(
'rel' => 'self',
'type' => 'application/activity+json',
@ -53,10 +58,12 @@ class Webfinger {
* @return array the jrd array
*/
public static function add_pseudo_user_discovery( $array, $resource ) {
if ( $array ) {
$user = Webfinger_Rest::get_profile( $resource );
if ( ! $user || is_wp_error( $user ) ) {
return $array;
}
return Webfinger_Rest::get_profile( $resource );
return $user;
}
}

View File

@ -1,9 +1,9 @@
=== ActivityPub ===
Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nuriapena, cavalierlife
Tags: OStatus, fediverse, activitypub, activitystream
Requires at least: 4.7
Requires at least: 5.5
Tested up to: 6.4
Stable tag: 1.3.0
Stable tag: 2.0.1
Requires PHP: 5.6
License: MIT
License URI: http://opensource.org/licenses/MIT
@ -68,10 +68,10 @@ Implemented:
* share posts
* receive comments/reactions
* signature verification
* threaded comments support
To implement:
* threaded comments support
* replace shortcodes with blocks for layout
= What is "ActivityPub for WordPress" =
@ -101,10 +101,34 @@ Add the following to the site.conf in sites-available:
Where 'blog' is the path to the subdirectory at which your blog resides.
= What if you are running your blog in a subdirectory, but have a different [wp_siteurl](https://wordpress.org/documentation/article/giving-wordpress-its-own-directory/)? =
In that case you don't need the redirect, because the index.php will take care of that.
== Changelog ==
Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub).
= 2.0.1 =
* Fixed: Comment `Update` Federation
* Workaround: Re-Added Post Model Class because of some weird caching issues
* Fixed: WebFinger check
* Fixed: Classic editor image finding for large images
= 2.0.0 =
* Added: Bidirectional Comment Federation
* Removed: Deprecated Classes
* Fixed: Normalize attributes that can have mixed value types
* Added: URL support for WebFinger
* Added: Make Post-Template filterable
* Added: CSS class for ActivityPub comments to allow custom designs
* Added: FEP-2677: Identifying the Application Actor
* Added: FEP-2c59: Discovery of a Webfinger address from an ActivityPub actor
* Added: Profile Update Activities
* Improved: WebFinger endpoints
= 1.3.0 =
* Added: Threaded-Comments support

View File

@ -10,23 +10,8 @@ $user->set_context(
*/
\do_action( 'activitypub_json_author_pre', $user->get__id() );
$options = 0;
// JSON_PRETTY_PRINT added in PHP 5.4
if ( \get_query_var( 'pretty' ) ) {
$options |= \JSON_PRETTY_PRINT; // phpcs:ignore
}
$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
/*
* Options to be passed to json_encode()
*
* @param int $options The current options flags
*/
$options = \apply_filters( 'activitypub_json_author_options', $options, $user->get__id() );
\header( 'Content-Type: application/activity+json' );
echo \wp_json_encode( $user->to_array(), $options );
echo $user->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
/*
* Action triggerd after the ActivityPub profile has been created and sent to the client

View File

@ -10,23 +10,8 @@ $user->set_context(
*/
\do_action( 'activitypub_json_author_pre', $user->get__id() );
$options = 0;
// JSON_PRETTY_PRINT added in PHP 5.4
if ( \get_query_var( 'pretty' ) ) {
$options |= \JSON_PRETTY_PRINT; // phpcs:ignore
}
$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
/*
* Options to be passed to json_encode()
*
* @param int $options The current options flags
*/
$options = \apply_filters( 'activitypub_json_author_options', $options, $user->get__id() );
\header( 'Content-Type: application/activity+json' );
echo \wp_json_encode( $user->to_array(), $options );
echo $user->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
/*
* Action triggerd after the ActivityPub profile has been created and sent to the client

View File

@ -0,0 +1,36 @@
<?php
$comment = \get_comment( \get_query_var( 'c', null ) ); // phpcs:ignore
$object = \Activitypub\Transformer\Factory::get_transformer( $comment );
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $object->to_object()->to_array() );
// filter output
$json = \apply_filters( 'activitypub_json_comment_array', $json );
/*
* Action triggerd prior to the ActivityPub profile being created and sent to the client
*/
\do_action( 'activitypub_json_comment_pre' );
$options = 0;
// JSON_PRETTY_PRINT added in PHP 5.4
if ( \get_query_var( 'pretty' ) ) {
$options |= \JSON_PRETTY_PRINT; // phpcs:ignore
}
$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
/*
* Options to be passed to json_encode()
*
* @param int $options The current options flags
*/
$options = \apply_filters( 'activitypub_json_comment_options', $options );
\header( 'Content-Type: application/activity+json' );
echo \wp_json_encode( $json, $options );
/*
* Action triggerd after the ActivityPub profile has been created and sent to the client
*/
\do_action( 'activitypub_json_comment_comment' );

View File

@ -2,34 +2,16 @@
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$post = \get_post();
$object = new \Activitypub\Transformer\Post( $post );
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $object->to_object()->to_array() );
// filter output
$json = \apply_filters( 'activitypub_json_post_array', $json );
$post_object = \Activitypub\Transformer\Factory::get_transformer( $post )->to_object();
$post_object->set_context( \Activitypub\get_context() );
/*
* Action triggerd prior to the ActivityPub profile being created and sent to the client
*/
\do_action( 'activitypub_json_post_pre' );
$options = 0;
// JSON_PRETTY_PRINT added in PHP 5.4
if ( \get_query_var( 'pretty' ) ) {
$options |= \JSON_PRETTY_PRINT; // phpcs:ignore
}
$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
/*
* Options to be passed to json_encode()
*
* @param int $options The current options flags
*/
$options = \apply_filters( 'activitypub_json_post_options', $options );
\header( 'Content-Type: application/activity+json' );
echo \wp_json_encode( $json, $options );
echo $post_object->to_json(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
/*
* Action triggerd after the ActivityPub profile has been created and sent to the client

View File

@ -11,11 +11,11 @@ $user = \Activitypub\Collection\Users::get_by_id( \get_current_user_id() ); ?>
</th>
<td>
<p>
<code><?php echo \esc_html( $user->get_resource() ); ?></code> or
<code><?php echo \esc_html( $user->get_webfinger() ); ?></code> or
<code><?php echo \esc_url( $user->get_url() ); ?></code>
</p>
<?php // translators: the webfinger resource ?>
<p class="description"><?php \printf( \esc_html__( 'Follow "@%s" by searching for it on Mastodon, Friendica, etc.', 'activitypub' ), \esc_html( $user->get_resource() ) ); ?></p>
<p class="description"><?php \printf( \esc_html__( 'Follow "@%s" by searching for it on Mastodon, Friendica, etc.', 'activitypub' ), \esc_html( $user->get_webfinger() ) ); ?></p>
</td>
</tr>
<tr class="activitypub-user-description-wrap">

View File

@ -30,7 +30,7 @@
<label for="activitypub-blog-identifier"><?php \esc_html_e( 'Username', 'activitypub' ); ?></label>
</p>
<p>
<input type="text" class="regular-text" id="activitypub-blog-identifier" value="<?php echo \esc_attr( $blog_user->get_resource() ); ?>" readonly />
<input type="text" class="regular-text" id="activitypub-blog-identifier" value="<?php echo \esc_attr( $blog_user->get_webfinger() ); ?>" readonly />
</p>
<p>
<label for="activitypub-blog-url"><?php \esc_html_e( 'Profile URL', 'activitypub' ); ?></label>
@ -62,7 +62,7 @@
<label for="activitypub-user-identifier"><?php \esc_html_e( 'Username', 'activitypub' ); ?></label>
</p>
<p>
<input type="text" class="regular-text" id="activitypub-user-identifier" value="<?php echo \esc_attr( $user->get_resource() ); ?>" readonly />
<input type="text" class="regular-text" id="activitypub-user-identifier" value="<?php echo \esc_attr( $user->get_webfinger() ); ?>" readonly />
</p>
<p>
<label for="activitypub-user-url"><?php \esc_html_e( 'Profile URL', 'activitypub' ); ?></label>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^9.5.21" installed="9.5.21" location="./tools/phpunit" copy="true"/>
<phar name="phpcs" version="^3.7.1" installed="3.7.1" location="./tools/phpcs" copy="true"/>
<phar name="phpcbf" version="^3.7.1" installed="3.7.1" location="./tools/phpcbf" copy="true"/>
</phive>

View File

@ -1,7 +0,0 @@
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,95 +0,0 @@
# authLDAP
[![Join the chat at https://gitter.im/heiglandreas/authLdap](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/heiglandreas/authLdap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Use your existing LDAP as authentication-backend for your wordpress!
[![Build Status](https://github.com/heiglandreas/authLdap/actions/workflows/tests.yml/badge.svg)](https://github.com/heiglandreas/authLdap/actions/workflows/tests.yml)
[![WordPress Stats](https://img.shields.io/wordpress/plugin/dt/authldap.svg)](https://wordpress.org/plugins/authldap/stats/)
[![WordPress Version](https://img.shields.io/wordpress/plugin/v/authldap.svg)](https://wordpress.org/plugins/authldap/)
[![WordPress testet](https://img.shields.io/wordpress/v/authldap.svg)](https://wordpress.org/plugins/authldap/)
[![Code Climate](https://codeclimate.com/github/heiglandreas/authLdap/badges/gpa.svg)](https://codeclimate.com/github/heiglandreas/authLdap)
[![codecov](https://codecov.io/gh/heiglandreas/authLdap/branch/master/graph/badge.svg?token=AYAhEeWtRQ)](https://codecov.io/gh/heiglandreas/authLdap)
So what are the differences to other Wordpress-LDAP-Authentication-Plugins?
* **Flexible**: You are totaly free in which LDAP-backend to use. Due to the extensive configuration you can
freely decide how to do the authentication of your users. It simply depends on your
filters
* **Independent**: As soon as a user logs in, it is added/updated to the Wordpress' user-database
to allow wordpress to always use the correct data. You only have to administer your users once.
* **Failsafe**: Due to the users being created in Wordpress' User-database they can
also log in when the LDAP-backend currently is gone.
* **Role-Aware**: You can map Wordpress' roles to values of an existing LDAP-attribute.
## How does the plugin work?
Well, as a matter of fact it is rather simple. The plugin verifies, that the user
seeking authentification can bind to the LDAP using the provided password.
If that is so, the user is either created or updated in the wordpress-user-database.
This update includes the provided password (so the wordpress can authenticate users
even without the LDAP), the users name according to the authLDAP-preferences and
the status of the user depending on the groups-settings of the authLDAP-preferences
Writing this plugin would not have been as easy as it has been, without the
wonderfull plugin of Alistair Young from http://www.weblogs.uhi.ac.uk/sm00ay/?p=45
## Configuration
### Usage Settings
* **Enable Authentication via LDAP** Whether you want to enable authLdap for login or not
* **debug authLdap** When you have problems with authentication via LDAP you can enable a debugging mode here.
* **Save entered Password** Decide whether passwords will be cached in your wordpress-installation. **Attention:** Without the cache your users will not be able to log into your site when your LDAP is down!
### Server Settings
* **LDAP Uri** This is the URI where your ldap-backend can be reached. More information are actually on the Configuration page
* **Filter** This is the real McCoy! The filter you define here specifies how a user will be found. Before applying the filter a %s will be replaced with the given username. This means, when a user logs in using foobar as username the following happens:
* **uid=%1$s** check for any LDAP-Entry that has an attribute uid with value foobar
* **(&(objectclass=posixAccount)(|(uid=%1$s)(mail=%1$s)))** check for any LDAP-Entry that has an attribute objectclass with value posixAccout and either a UID- or a mail-attribute with value foobar
This filter is rather powerfull if used wisely.
### Creating Users
* **Name-Attribute** Which Attribute from the LDAP contains the Full or the First name of the user trying to log in. This defaults to name
* **Second Name Attribute** If the above Name-Attribute only contains the First Name of the user you can here specify an Attribute that contains the second name. This field is empty by default
* **User-ID Attribute** This field will be used as login-name for wordpress. Please give the Attribute, that is used to identify the user. This should be the same as you used in the above Filter-Option. This field defaults to uid
* **Mail Attribute** Which Attribute holds the eMail-Address of the user? If more than one eMail-Address are stored in the LDAP, only the first given is used. This field defaults to mail
* **Web-Attribute** If your users have a personal page (URI) stored in the LDAP, it can be provided here. This field is empty by default
### User-Groups for Roles
* **Group-Attribute** This is the attribute that defines the Group-ID that can be matched against the Groups defined further down This field defaults to gidNumber.
* **Group-Filter** Here you can add the filter for selecting groups for the currentlly logged in user The Filter should contain the string %s which will be replaced by the login-name of the currently logged in
## FAQ
<dl>
<dt>Can I change a users password with this plugin?</dt>
<dd>Short Answer: <strong>No</strong>!<br>Long Answer: As the users credentials are not
only used for a wordpress-site when you authenticate against an LDAP but for
many other services also chances are great that there is a centralized place
where password-changes shall be made. We'll later allow inclusion of a link
to such a place but currently it's not available. And as password-hashing and
where to store it requires deeper insight into the LDAP-Server then most users
have and admins are willing to give, password changes are out of scope of this
plugin. If you know exactyl what you do, you might want to have a look at
<a href="https://github.com/heiglandreas/authLdap/issues/54#issuecomment-125851029">
issue 54</a>
wherer a way of adding it is described!
</dd>
<dt>Can I add a user to the LDAP when she creates a user-account on wordpress?</dt>
<dd>Short Answer: <strong>No</strong>!<br>Long Answer: Even though that is technically possible
it's not in the scope of this plugin. As creating a user in an LDAP often involves
an administrative process that has already been implemented in your departments
administration it doesn't make sense to rebuild that - in most cases highly
individual - process in this plugin. If you know exactly what you do, have a look at
<a href="https://github.com/heiglandreas/authLdap/issues/65">issue 65</a>
where <a href="https://github.com/wtfiwtz">wtfiwtz</a> shows how to implement that feature.
</dd>
</dl>

View File

@ -1,18 +0,0 @@
# Security-Policy
## Supported Versions
| Version | Supported |
| ------- |--------------------|
| 2.x | :white_check_mark: |
| 1.x | :x: |
## Reporting a Vulnerability
* Check our security.txt file for details on how to contact us
* Contact us before publicly disclosing the issue anywhere else
This plugin is developed as OpenSource under the MIT licence.
There is no money earned from it. Therefore we are not able to
provide any bug-bounties whatsoever. You will be mentioned in the
release notes of a fix-release though.

View File

@ -1,13 +0,0 @@
.row {
overflow: hidden;
padding-top: 10px;
}
.element {
float: right;
text-align: left;
}
.authldap-options input[type=text] {
width: 100%;
}

View File

@ -1,946 +0,0 @@
<?php
/*
Plugin Name: AuthLDAP
Plugin URI: https://github.com/heiglandreas/authLdap
Description: This plugin allows you to use your existing LDAP as authentication base for WordPress
Version: 2.5.9
Author: Andreas Heigl <andreas@heigl.org>
Author URI: http://andreas.heigl.org
License: MIT
License URI: https://opensource.org/licenses/MIT
*/
// phpcs:disable PSR1.Files.SideEffects
use Org_Heigl\AuthLdap\LdapList;
use Org_Heigl\AuthLdap\LdapUri;
use Org_Heigl\AuthLdap\Manager\Ldap;
use Org_Heigl\AuthLdap\UserRoleHandler;
use Org_Heigl\AuthLdap\Wrapper\LdapFactory;
require_once __DIR__ . '/src/Wrapper/LdapInterface.php';
require_once __DIR__ . '/src/Exception/Error.php';
require_once __DIR__ . '/src/Exception/InvalidLdapUri.php';
require_once __DIR__ . '/src/Exception/Error.php';
require_once __DIR__ . '/src/Exception/InvalidLdapUri.php';
require_once __DIR__ . '/src/Exception/MissingValidLdapConnection.php';
require_once __DIR__ . '/src/Exception/SearchUnsuccessfull.php';
require_once __DIR__ . '/src/Manager/Ldap.php';
require_once __DIR__ . '/src/Wrapper/Ldap.php';
require_once __DIR__ . '/src/Wrapper/LdapFactory.php';
require_once __DIR__ . '/src/LdapList.php';
require_once __DIR__ . '/src/LdapUri.php';
require_once __DIR__ . '/src/UserRoleHandler.php';
function authLdap_debug($message)
{
if (authLdap_get_option('Debug')) {
error_log('[AuthLDAP] ' . $message, 0);
}
}
function authLdap_addmenu()
{
if (!is_multisite()) {
add_options_page(
'AuthLDAP',
'AuthLDAP',
'manage_options',
basename(__FILE__),
'authLdap_options_panel'
);
} else {
add_submenu_page(
'settings.php',
'AuthLDAP',
'AuthLDAP',
'manage_options',
'authldap',
'authLdap_options_panel'
);
}
}
function authLdap_get_post($name, $default = '')
{
return isset($_POST[$name]) ? $_POST[$name] : $default;
}
function authLdap_options_panel()
{
// inclusde style sheet
wp_enqueue_style('authLdap-style', plugin_dir_url(__FILE__) . 'authLdap.css');
if (($_SERVER['REQUEST_METHOD'] == 'POST') && array_key_exists('ldapOptionsSave', $_POST)) {
if (!isset($_POST['authLdapNonce'])) {
die("Go away!");
}
if (!wp_verify_nonce($_POST['authLdapNonce'],'authLdapNonce')) {
die("Go away!");
}
$new_options = [
'Enabled' => authLdap_get_post('authLDAPAuth', false),
'CachePW' => authLdap_get_post('authLDAPCachePW', false),
'URI' => authLdap_get_post('authLDAPURI'),
'URISeparator' => authLdap_get_post('authLDAPURISeparator'),
'StartTLS' => authLdap_get_post('authLDAPStartTLS', false),
'Filter' => authLdap_get_post('authLDAPFilter'),
'NameAttr' => authLdap_get_post('authLDAPNameAttr'),
'SecName' => authLdap_get_post('authLDAPSecName'),
'UidAttr' => authLdap_get_post('authLDAPUidAttr'),
'MailAttr' => authLdap_get_post('authLDAPMailAttr'),
'WebAttr' => authLdap_get_post('authLDAPWebAttr'),
'Groups' => authLdap_get_post('authLDAPGroups', []),
'GroupSeparator' => authLdap_get_post('authLDAPGroupSeparator', ','),
'Debug' => authLdap_get_post('authLDAPDebug', false),
'GroupBase' => authLdap_get_post('authLDAPGroupBase'),
'GroupAttr' => authLdap_get_post('authLDAPGroupAttr'),
'GroupFilter' => authLdap_get_post('authLDAPGroupFilter'),
'DefaultRole' => authLdap_get_post('authLDAPDefaultRole'),
'GroupEnable' => authLdap_get_post('authLDAPGroupEnable', false),
'GroupOverUser' => authLdap_get_post('authLDAPGroupOverUser', false),
'DoNotOverwriteNonLdapUsers' => authLdap_get_post('authLDAPDoNotOverwriteNonLdapUsers', false),
'UserRead' => authLdap_get_post('authLDAPUseUserAccount', false),
];
if (authLdap_set_options($new_options)) {
echo "<div class='updated'><p>Saved Options!</p></div>";
} else {
echo "<div class='error'><p>Could not save Options!</p></div>";
}
}
// Do some initialization for the admin-view
$authLDAP = authLdap_get_option('Enabled');
$authLDAPCachePW = authLdap_get_option('CachePW');
$authLDAPURI = authLdap_get_option('URI');
$authLDAPURISeparator = authLdap_get_option('URISeparator');
$authLDAPStartTLS = authLdap_get_option('StartTLS');
$authLDAPFilter = authLdap_get_option('Filter');
$authLDAPNameAttr = authLdap_get_option('NameAttr');
$authLDAPSecName = authLdap_get_option('SecName');
$authLDAPMailAttr = authLdap_get_option('MailAttr');
$authLDAPUidAttr = authLdap_get_option('UidAttr');
$authLDAPWebAttr = authLdap_get_option('WebAttr');
$authLDAPGroups = authLdap_get_option('Groups');
$authLDAPGroupSeparator = authLdap_get_option('GroupSeparator');
$authLDAPDebug = authLdap_get_option('Debug');
$authLDAPGroupBase = authLdap_get_option('GroupBase');
$authLDAPGroupAttr = authLdap_get_option('GroupAttr');
$authLDAPGroupFilter = authLdap_get_option('GroupFilter');
$authLDAPDefaultRole = authLdap_get_option('DefaultRole');
$authLDAPGroupEnable = authLdap_get_option('GroupEnable');
$authLDAPGroupOverUser = authLdap_get_option('GroupOverUser');
$authLDAPDoNotOverwriteNonLdapUsers = authLdap_get_option('DoNotOverwriteNonLdapUsers');
$authLDAPUseUserAccount = authLdap_get_option('UserRead');
$tChecked = ($authLDAP) ? ' checked="checked"' : '';
$tDebugChecked = ($authLDAPDebug) ? ' checked="checked"' : '';
$tPWChecked = ($authLDAPCachePW) ? ' checked="checked"' : '';
$tGroupChecked = ($authLDAPGroupEnable) ? ' checked="checked"' : '';
$tGroupOverUserChecked = ($authLDAPGroupOverUser) ? ' checked="checked"' : '';
$tStartTLSChecked = ($authLDAPStartTLS) ? ' checked="checked"' : '';
$tDoNotOverwriteNonLdapUsers = ($authLDAPDoNotOverwriteNonLdapUsers) ? ' checked="checked"' : '';
$tUserRead = ($authLDAPUseUserAccount) ? ' checked="checked"' : '';
$roles = new WP_Roles();
$action = $_SERVER['REQUEST_URI'];
if (!extension_loaded('ldap')) {
echo '<div class="warning">The LDAP-Extension is not available on your '
. 'WebServer. Therefore Everything you can alter here does not '
. 'make any sense!</div>';
}
include dirname(__FILE__) . '/view/admin.phtml';
}
/**
* get a LDAP server object
*
* throws exception if there is a problem connecting
*
* @conf boolean authLDAPDebug true, if debugging should be turned on
* @conf string authLDAPURI LDAP server URI
*
* @return Org_Heigl\AuthLdap\LdapList LDAP server object
*/
function authLdap_get_server()
{
static $_ldapserver = null;
if (is_null($_ldapserver)) {
$authLDAPDebug = authLdap_get_option('Debug');
$authLDAPURI = explode(
authLdap_get_option('URISeparator', ' '),
authLdap_get_option('URI')
);
$authLDAPStartTLS = authLdap_get_option('StartTLS');
//$authLDAPURI = 'ldap:/foo:bar@server/trallala';
authLdap_debug('connect to LDAP server');
require_once dirname(__FILE__) . '/src/LdapList.php';
$_ldapserver = new LdapList();
foreach ($authLDAPURI as $uri) {
$_ldapserver->addLdap(new Ldap(
new LdapFactory(),
LdapUri::fromString($uri),
$authLDAPStartTLS
));
}
}
return $_ldapserver;
}
/**
* This method authenticates a user using either the LDAP or, if LDAP is not
* available, the local database
*
* For this we store the hashed passwords in the WP_Database to ensure working
* conditions even without an LDAP-Connection
*
* @param null|WP_User|WP_Error
* @param string $username
* @param string $password
* @param boolean $already_md5
* @return boolean true, if login was successfull or false, if it wasn't
* @conf boolean authLDAP true, if authLDAP should be used, false if not. Defaults to false
* @conf string authLDAPFilter LDAP filter to use to find correct user, defaults to '(uid=%s)'
* @conf string authLDAPNameAttr LDAP attribute containing user (display) name, defaults to 'name'
* @conf string authLDAPSecName LDAP attribute containing second name, defaults to ''
* @conf string authLDAPMailAttr LDAP attribute containing user e-mail, defaults to 'mail'
* @conf string authLDAPUidAttr LDAP attribute containing user id (the username we log on with), defaults to 'uid'
* @conf string authLDAPWebAttr LDAP attribute containing user website, defaults to ''
* @conf string authLDAPDefaultRole default role for authenticated user, defaults to ''
* @conf boolean authLDAPGroupEnable true, if we try to map LDAP groups to Wordpress roles
* @conf boolean authLDAPGroupOverUser true, if LDAP Groups have precedence over existing user roles
*/
function authLdap_login($user, $username, $password, $already_md5 = false)
{
// don't do anything when authLDAP is disabled
if (!authLdap_get_option('Enabled')) {
authLdap_debug(
'LDAP disabled in AuthLDAP plugin options (use the first option in the AuthLDAP options to enable it)'
);
return $user;
}
// If the user has already been authenticated (only in that case we get a
// WP_User-Object as $user) we skip LDAP-authentication and simply return
// the existing user-object
if ($user instanceof WP_User) {
authLdap_debug(sprintf(
'User %s has already been authenticated - skipping LDAP-Authentication',
$user->get('nickname')
));
return $user;
}
authLdap_debug("User '$username' logging in");
if ($username == 'admin') {
authLdap_debug('Doing nothing for possible local user admin');
return $user;
}
global $wpdb, $error;
try {
$authLDAP = authLdap_get_option('Enabled');
$authLDAPFilter = authLdap_get_option('Filter');
$authLDAPNameAttr = authLdap_get_option('NameAttr');
$authLDAPSecName = authLdap_get_option('SecName');
$authLDAPMailAttr = authLdap_get_option('MailAttr');
$authLDAPUidAttr = authLdap_get_option('UidAttr');
$authLDAPWebAttr = authLdap_get_option('WebAttr');
$authLDAPDefaultRole = authLdap_get_option('DefaultRole');
$authLDAPGroupEnable = authLdap_get_option('GroupEnable');
$authLDAPGroupOverUser = authLdap_get_option('GroupOverUser');
$authLDAPUseUserAccount = authLdap_get_option('UserRead');
if (!$username) {
authLdap_debug('Username not supplied: return false');
return false;
}
if (!$password) {
authLdap_debug('Password not supplied: return false');
$error = __('<strong>Error</strong>: The password field is empty.');
return false;
}
// First check for valid values and set appropriate defaults
if (!$authLDAPFilter) {
$authLDAPFilter = '(uid=%s)';
}
if (!$authLDAPNameAttr) {
$authLDAPNameAttr = 'name';
}
if (!$authLDAPMailAttr) {
$authLDAPMailAttr = 'mail';
}
if (!$authLDAPUidAttr) {
$authLDAPUidAttr = 'uid';
}
// If already_md5 is TRUE, then we're getting the user/password from the cookie. As we don't want
// to store LDAP passwords in any
// form, we've already replaced the password with the hashed username and LDAP_COOKIE_MARKER
if ($already_md5) {
if ($password == md5($username) . md5($ldapCookieMarker)) {
authLdap_debug('cookie authentication');
return true;
}
}
// Remove slashes as noted on https://github.com/heiglandreas/authLdap/issues/108
$password = stripslashes_deep($password);
// No cookie, so have to authenticate them via LDAP
$result = false;
try {
authLdap_debug('about to do LDAP authentication');
$result = authLdap_get_server()->Authenticate($username, $password, $authLDAPFilter);
} catch (Exception $e) {
authLdap_debug('LDAP authentication failed with exception: ' . $e->getMessage());
return false;
}
// Make optional querying from the admin account #213
if (!authLdap_get_option('UserRead')) {
// Rebind with the default credentials after the user has been loged in
// Otherwise the credentials of the user trying to login will be used
// This fixes #55
authLdap_get_server()->bind();
}
if (true !== $result) {
authLdap_debug('LDAP authentication failed');
// TODO what to return? WP_User object, true, false, even an WP_Error object...
// all seem to fall back to normal wp user authentication
return;
}
authLdap_debug('LDAP authentication successful');
$attributes = array_values(
array_filter(
apply_filters(
'authLdap_filter_attributes',
[
$authLDAPNameAttr,
$authLDAPSecName,
$authLDAPMailAttr,
$authLDAPWebAttr,
$authLDAPUidAttr,
]
)
)
);
try {
$attribs = authLdap_get_server()->search(
sprintf($authLDAPFilter, $username),
$attributes
);
// First get all the relevant group informations so we can see if
// whether have been changes in group association of the user
if (!isset($attribs[0]['dn'])) {
authLdap_debug('could not get user attributes from LDAP');
throw new UnexpectedValueException('dn has not been returned');
}
if (!isset($attribs[0][strtolower($authLDAPUidAttr)][0])) {
authLdap_debug('could not get user attributes from LDAP');
throw new UnexpectedValueException('The user-ID attribute has not been returned');
}
$dn = $attribs[0]['dn'];
$realuid = $attribs[0][strtolower($authLDAPUidAttr)][0];
} catch (Exception $e) {
authLdap_debug('Exception getting LDAP user: ' . $e->getMessage());
return false;
}
$uid = authLdap_get_uid($realuid);
// This fixes #172
if (true == authLdap_get_option('DoNotOverwriteNonLdapUsers', false)) {
if (!get_user_meta($uid, 'authLDAP')) {
return null;
}
}
$roles = [];
// we only need this if either LDAP groups are disabled or
// if the WordPress role of the user overrides LDAP groups
if (!$authLDAPGroupEnable || !$authLDAPGroupOverUser) {
$role = authLdap_user_role($uid);
if ($role !== '') {
$roles[] = $role;
}
// TODO, this needs to be revised, it seems, like authldap is taking only the first role
// even if in WP there are assigned multiple.
}
// do LDAP group mapping if needed
// (if LDAP groups override worpress user role, $role is still empty)
if (empty($roles) && $authLDAPGroupEnable) {
$mappedRoles = authLdap_groupmap($realuid, $dn);
if ($mappedRoles !== []) {
$roles = $mappedRoles;
authLdap_debug('role from group mapping: ' . json_encode($roles));
}
}
// if we don't have a role yet, use default role
if (empty($roles) && !empty($authLDAPDefaultRole)) {
authLdap_debug('no role yet, set default role');
$roles[] = $authLDAPDefaultRole;
}
if (empty($roles)) {
// Sorry, but you are not in any group that is allowed access
trigger_error('no group found');
authLdap_debug('user is not in any group that is allowed access');
return false;
} else {
$wp_roles = new WP_Roles();
// not sure if this is needed, but it can't hurt
// Get rid of unexisting roles.
foreach ($roles as $k => $v) {
if (!$wp_roles->is_role($v)) {
unset($k);
}
}
// check if single role or an empty array provided
if (empty($roles)) {
trigger_error('no group found');
authLdap_debug('role is invalid');
return false;
}
}
// from here on, the user has access!
// now, lets update some user details
$user_info = [];
$user_info['user_login'] = $realuid;
$user_info['user_email'] = '';
$user_info['user_nicename'] = '';
// first name
if (isset($attribs[0][strtolower($authLDAPNameAttr)][0])) {
$user_info['first_name'] = $attribs[0][strtolower($authLDAPNameAttr)][0];
}
// last name
if (isset($attribs[0][strtolower($authLDAPSecName)][0])) {
$user_info['last_name'] = $attribs[0][strtolower($authLDAPSecName)][0];
}
// mail address
if (isset($attribs[0][strtolower($authLDAPMailAttr)][0])) {
$user_info['user_email'] = $attribs[0][strtolower($authLDAPMailAttr)][0];
}
// website
if (isset($attribs[0][strtolower($authLDAPWebAttr)][0])) {
$user_info['user_url'] = $attribs[0][strtolower($authLDAPWebAttr)][0];
}
// display name, nickname, nicename
if (array_key_exists('first_name', $user_info)) {
$user_info['display_name'] = $user_info['first_name'];
$user_info['nickname'] = $user_info['first_name'];
$user_info['user_nicename'] = sanitize_title_with_dashes($user_info['first_name']);
if (array_key_exists('last_name', $user_info)) {
$user_info['display_name'] .= ' ' . $user_info['last_name'];
$user_info['nickname'] .= ' ' . $user_info['last_name'];
$user_info['user_nicename'] .= '_' . sanitize_title_with_dashes($user_info['last_name']);
}
}
$user_info['user_nicename'] = substr($user_info['user_nicename'], 0, 50);
// optionally store the password into the wordpress database
if (authLdap_get_option('CachePW')) {
// Password will be hashed inside wp_update_user or wp_insert_user
$user_info['user_pass'] = $password;
} else {
// clear the password
$user_info['user_pass'] = '';
}
// add uid if user exists
if ($uid) {
// found user in the database
authLdap_debug('The LDAP user has an entry in the WP-Database');
$user_info['ID'] = $uid;
unset($user_info['display_name'], $user_info['nickname']);
$userid = wp_update_user($user_info);
} else {
// new wordpress account will be created
authLdap_debug('The LDAP user does not have an entry in the WP-Database, a new WP account will be created');
$userid = wp_insert_user($user_info);
}
// if the user exists, wp_insert_user will update the existing user record
if (is_wp_error($userid)) {
authLdap_debug('Error creating user : ' . $userid->get_error_message());
trigger_error('Error creating user: ' . $userid->get_error_message());
return $userid;
}
// Update user roles.
$user = new \WP_User($userid);
/**
* Add hook for custom User-Role assignment
*
* @param WP_User $user This user-object will be returned. Can be modified as necessary in the actions.
* @param array $roles
*/
do_action('authldap_user_roles', $user, $roles);
/**
* Add hook for custom updates
*
* @param int $userid User ID.
* @param array $attribs [0] Attributes retrieved from LDAP for the user.
*/
do_action('authLdap_login_successful', $userid, $attribs[0]);
authLdap_debug('user id = ' . $userid);
// flag the user as an ldap user so we can hide the password fields in the user profile
update_user_meta($userid, 'authLDAP', true);
// return a user object upon positive authorization
return $user;
} catch (Exception $e) {
authLdap_debug($e->getMessage() . '. Exception thrown in line ' . $e->getLine());
trigger_error($e->getMessage() . '. Exception thrown in line ' . $e->getLine());
}
}
/**
* Get user's user id
*
* Returns null if username not found
*
* @param string $username username
* @param string user id, null if not found
*/
function authLdap_get_uid($username)
{
global $wpdb;
// find out whether the user is already present in the database
$uid = $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->users} WHERE user_login = %s",
$username
)
);
if ($uid) {
authLdap_debug("Existing user, uid = {$uid}");
return $uid;
} else {
return null;
}
}
/**
* Get the user's current role
*
* Returns empty string if not found.
*
* @param int $uid wordpress user id
* @return string role, empty if none found
*/
function authLdap_user_role($uid)
{
global $wpdb, $wp_roles;
if (!$uid) {
return '';
}
/** @var array<string, bool> $usercapabilities */
$usercapabilities = get_user_meta($uid, "{$wpdb->prefix}capabilities", true);
if (!is_array($usercapabilities)) {
return '';
}
/** @var array<string, array{name: string, capabilities: array<mixed>} $editable_roles */
$editable_roles = $wp_roles->roles;
// By using this approach we are now using the order of the roles from the WP_Roles object
// and not from the capabilities any more.
$userroles = array_keys(array_intersect_key($editable_roles, $usercapabilities));
$role = ($userroles !== []) ? $userroles[0] : '';
authLdap_debug("Existing user's role: {$role}");
return $role;
}
/**
* Get LDAP groups for user and map to role
*
* @param string $username
* @param string $dn
* @return array role, empty array if no mapping found, first or all role(s) found otherwise
* @conf array authLDAPGroups, associative array, role => ldap_group
* @conf string authLDAPGroupBase, base dn to look up groups
* @conf string authLDAPGroupAttr, ldap attribute that holds name of group
* @conf string authLDAPGroupFilter, LDAP filter to find groups. can contain %s and %dn% placeholders
*/
function authLdap_groupmap($username, $dn)
{
$authLDAPGroups = authLdap_sort_roles_by_capabilities(
authLdap_get_option('Groups')
);
$authLDAPGroupBase = authLdap_get_option('GroupBase');
$authLDAPGroupAttr = authLdap_get_option('GroupAttr');
$authLDAPGroupFilter = authLdap_get_option('GroupFilter');
$authLDAPGroupSeparator = authLdap_get_option('GroupSeparator');
if (!$authLDAPGroupAttr) {
$authLDAPGroupAttr = 'gidNumber';
}
if (!$authLDAPGroupFilter) {
$authLDAPGroupFilter = '(&(objectClass=posixGroup)(memberUid=%s))';
}
if (!$authLDAPGroupSeparator) {
$authLDAPGroupSeparator = ',';
}
if (!is_array($authLDAPGroups) || count(array_filter(array_values($authLDAPGroups))) == 0) {
authLdap_debug('No group names defined');
return [];
}
try {
// To allow searches based on the DN instead of the uid, we replace the
// string %dn% with the users DN.
$authLDAPGroupFilter = str_replace(
'%dn%',
ldap_escape($dn, '', LDAP_ESCAPE_FILTER),
$authLDAPGroupFilter
);
authLdap_debug('Group Filter: ' . json_encode($authLDAPGroupFilter));
authLdap_debug('Group Base: ' . $authLDAPGroupBase);
$groups = authLdap_get_server()->search(
sprintf($authLDAPGroupFilter, ldap_escape($username, '', LDAP_ESCAPE_FILTER)),
[$authLDAPGroupAttr],
$authLDAPGroupBase
);
} catch (Exception $e) {
authLdap_debug('Exception getting LDAP group attributes: ' . $e->getMessage());
return [];
}
$grp = [];
for ($i = 0; $i < $groups ['count']; $i++) {
if ($authLDAPGroupAttr == "dn") {
$grp[] = $groups[$i]['dn'];
} else {
for ($k = 0; $k < $groups[$i][strtolower($authLDAPGroupAttr)]['count']; $k++) {
$grp[] = $groups[$i][strtolower($authLDAPGroupAttr)][$k];
}
}
}
authLdap_debug('LDAP groups: ' . json_encode($grp));
// Check whether the user is member of one of the groups that are
// allowed acces to the blog. If the user is not member of one of
// The groups throw her out! ;-)
$roles = [];
foreach ($authLDAPGroups as $key => $val) {
$currentGroup = explode($authLDAPGroupSeparator, $val);
// Remove whitespaces around the group-ID
$currentGroup = array_map('trim', $currentGroup);
if (0 < count(array_intersect($currentGroup, $grp))) {
$roles[] = $key;
}
}
// Default: If the user is member of more than one group only the first one
// will be taken into account!
// This filter allows you to return multiple user roles. WordPress
// supports this functionality, but not natively via UI from Users
// overview (you need to use a plugin). However, it's still widely used,
// for example, by WooCommerce, etc. Use if you know what you're doing.
if (apply_filters('authLdap_allow_multiple_roles', false) === false && count($roles) > 1) {
$roles = array_slice($roles, 0, 1);
}
authLdap_debug("Roles from LDAP group: " . json_encode($roles));
return $roles;
}
/**
* This function disables the password-change fields in the users preferences.
*
* It does not make sense to authenticate via LDAP and then allow the user to
* change the password only in the wordpress database. And changing the password
* LDAP-wide can not be the scope of Wordpress!
*
* Whether the user is an LDAP-User or not is determined using the authLDAP-Flag
* of the users meta-informations
*
* @return false, if the user whose prefs are viewed is an LDAP-User, true if
* he isn't
* @conf boolean authLDAP
*/
function authLdap_show_password_fields($return, $user)
{
if (!$user) {
return true;
}
if (get_user_meta($user->ID, 'authLDAP')) {
return false;
}
return $return;
}
/**
* This function disables the password reset for a user.
*
* It does not make sense to authenticate via LDAP and then allow the user to
* reset the password only in the wordpress database. And changing the password
* LDAP-wide can not be the scope of Wordpress!
*
* Whether the user is an LDAP-User or not is determined using the authLDAP-Flag
* of the users meta-informations
*
* @author chaplina (https://github.com/chaplina)
* @conf boolean authLDAP
* @return false, if the user is an LDAP-User, true if he isn't
*/
function authLdap_allow_password_reset($return, $userid)
{
if (!(isset($userid))) {
return true;
}
if (get_user_meta($userid, 'authLDAP')) {
return false;
}
return $return;
}
/**
* Sort the given roles by number of capabilities
*
* @param array $roles
*
* @return array
*/
function authLdap_sort_roles_by_capabilities($roles)
{
global $wpdb;
$myRoles = get_option($wpdb->get_blog_prefix() . 'user_roles');
authLdap_debug(print_r($roles, true));
uasort($myRoles, 'authLdap_sortByCapabilitycount');
$return = [];
foreach ($myRoles as $key => $role) {
if (isset($roles[$key])) {
$return[$key] = $roles[$key];
}
}
authLdap_debug(print_r($return, true));
return $return;
}
/**
* Sort according to the number of capabilities
*
* @param $a
* @param $b
*/
function authLdap_sortByCapabilitycount($a, $b)
{
if (count($a['capabilities']) > count($b['capabilities'])) {
return -1;
}
if (count($a['capabilities']) < count($b['capabilities'])) {
return 1;
}
return 0;
}
/**
* Load AuthLDAP Options
*
* Sets and stores defaults if options are not up to date
*/
function authLdap_load_options($reload = false)
{
static $options = null;
// the current version for options
$option_version_plugin = 1;
$optionFunction = 'get_option';
if (is_multisite()) {
$optionFunction = 'get_site_option';
}
if (is_null($options) || $reload) {
$options = $optionFunction('authLDAPOptions', []);
}
// check if option version has changed (or if it's there at all)
if (!isset($options['Version']) || ($options['Version'] != $option_version_plugin)) {
// defaults for all options
$options_default = [
'Enabled' => false,
'CachePW' => false,
'URI' => '',
'URISeparator' => ' ',
'Filter' => '', // '(uid=%s)'
'NameAttr' => '', // 'name'
'SecName' => '',
'UidAttr' => '', // 'uid'
'MailAttr' => '', // 'mail'
'WebAttr' => '',
'Groups' => [],
'Debug' => false,
'GroupAttr' => '', // 'gidNumber'
'GroupFilter' => '', // '(&(objectClass=posixGroup)(memberUid=%s))'
'DefaultRole' => '',
'GroupEnable' => true,
'GroupOverUser' => true,
'Version' => $option_version_plugin,
'DoNotOverwriteNonLdapUsers' => false,
];
// check if we got a version
if (!isset($options['Version'])) {
// we just changed to the new option format
// read old options, then delete them
$old_option_new_option = [
'authLDAP' => 'Enabled',
'authLDAPCachePW' => 'CachePW',
'authLDAPURI' => 'URI',
'authLDAPFilter' => 'Filter',
'authLDAPNameAttr' => 'NameAttr',
'authLDAPSecName' => 'SecName',
'authLDAPUidAttr' => 'UidAttr',
'authLDAPMailAttr' => 'MailAttr',
'authLDAPWebAttr' => 'WebAttr',
'authLDAPGroups' => 'Groups',
'authLDAPDebug' => 'Debug',
'authLDAPGroupAttr' => 'GroupAttr',
'authLDAPGroupFilter' => 'GroupFilter',
'authLDAPDefaultRole' => 'DefaultRole',
'authLDAPGroupEnable' => 'GroupEnable',
'authLDAPGroupOverUser' => 'GroupOverUser',
];
foreach ($old_option_new_option as $old_option => $new_option) {
$value = get_option($old_option, null);
if (!is_null($value)) {
$options[$new_option] = $value;
}
delete_option($old_option);
}
delete_option('authLDAPCookieMarker');
delete_option('authLDAPCookierMarker');
}
// set default for all options that are missing
foreach ($options_default as $key => $default) {
if (!isset($options[$key])) {
$options[$key] = $default;
}
}
// set new version and save
$options['Version'] = $option_version_plugin;
update_option('authLDAPOptions', $options);
}
return $options;
}
/**
* Get an individual option
*/
function authLdap_get_option($optionname, $default = null)
{
$options = authLdap_load_options();
if (isset($options[$optionname]) && $options[$optionname]) {
return $options[$optionname];
}
if (null !== $default) {
return $default;
}
//authLdap_debug('option name invalid: ' . $optionname);
return null;
}
/**
* Set new options
*/
function authLdap_set_options($new_options = [])
{
// initialize the options with what we currently have
$options = authLdap_load_options();
// set the new options supplied
foreach ($new_options as $key => $value) {
$options[$key] = $value;
}
// store options
$optionFunction = 'update_option';
if (is_multisite()) {
$optionFunction = 'update_site_option';
}
if ($optionFunction('authLDAPOptions', $options)) {
// reload the option cache
authLdap_load_options(true);
return true;
}
// could not set options
return false;
}
/**
* Do not send an email after changing the password or the email of the user!
*
* @param boolean $result The initial resturn value
* @param array $user The old userdata
* @param array $newUserData The changed userdata
*
* @return bool
*/
function authLdap_send_change_email($result, $user, $newUserData)
{
if (get_user_meta($user['ID'], 'authLDAP')) {
return false;
}
return $result;
}
$hook = is_multisite() ? 'network_' : '';
add_action($hook . 'admin_menu', 'authLdap_addmenu');
add_filter('show_password_fields', 'authLdap_show_password_fields', 10, 2);
add_filter('allow_password_reset', 'authLdap_allow_password_reset', 10, 2);
add_filter('authenticate', 'authLdap_login', 10, 3);
/** This only works from WP 4.3.0 on */
add_filter('send_password_change_email', 'authLdap_send_change_email', 10, 3);
add_filter('send_email_change_email', 'authLdap_send_change_email', 10, 3);
$handler = new UserRoleHandler();
add_action('authldap_user_roles', [$handler, 'addRolesToUser'], 10, 2);

View File

@ -1,22 +0,0 @@
<?xml version="1.0"?>
<ruleset name="Custom Standard" namespace="MyProject\CS\Standard">
<description>authLdap codestyle</description>
<file>./src</file>
<file>./authLdap.php</file>
<file>./tests</file>
<arg name="colors"/>
<arg value="sp"/>
<autoload>./vendor/autoload.php</autoload>
<rule ref="PSR12">
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
</rule>
<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="tabIndent" value="true"/>
</properties>
</rule>
</ruleset>

View File

@ -1,161 +0,0 @@
=== authLdap ===
Contributors: heiglandreas
Tags: ldap, auth, authentication, active directory, AD, openLDAP, Open Directory
Requires at least: 2.5.0
Tested up to: 6.3.0
Requires PHP: 7.4
Stable tag: trunk
License: MIT
License URI: https://opensource.org/licenses/MIT
Use your existing LDAP flexible as authentication backend for WordPress
== Description ==
Use your existing LDAP as authentication-backend for your wordpress!
So what are the differences to other Wordpress-LDAP-Authentication-Plugins?
* Flexible: You are totaly free in which LDAP-backend to use. Due to the extensive configuration you can freely decide how to do the authentication of your users. It simply depends on your filters
* Independent: As soon as a user logs in, it is added/updated to the Wordpress' user-database to allow wordpress to always use the correct data. You only have to administer your users once.
* Failsafe: Due to the users being created in Wordpress' User-database they can also log in when the LDAP-backend currently is gone.
* Role-Aware: You can map Wordpress' roles to values of an existing LDAP-attribute.
For more Information on the configuration have a look at https://github.com/heiglandreas/authLdap
== Installation ==
1. Upload the extracted folder `authLdap` to the `/wp-content/plugins/` directory
2. Activate the plugin through the 'Plugins' menu in WordPress
3. Configure the Plugin via the 'authLdap'-Configuration-Page.
== Frequently Asked Questions ==
= Where can I find more Informations about the plugin? =
Go to https://github.com/heiglandreas/authLdap
= Where can I report issues with the plugin? =
Please use the issuetracker at https://github.com/heiglandreas/authLdap/issues
= Where can I report sensitive security issues with the plugin? =
In essence: Report a security vulnerability at https://github.com/heiglandreas/authLdap/security/advisories/new
Please see https://github.com/heiglandreas/authLdap/blob/master/SECURITY.md for more details
== Changelog ==
= 2.5.9 =
* Adds information about security-contacts
* Addresses CVE-2023-41655
= 2.5.8 =
* Fix regression from 2.5.7
= 2.5.7 =
* Fix regressions from 2.5.4
* Fix CI system
= 2.5.4 =
* Update Tested up to
= 2.5.3 =
* Fix issue with broken role-assignement in combination with WooCommerce
* Fix spelling issue
* Allow DN as role-definition
= 2.5.0 =
* Ignore the order of capabilities to tell the role. In addition the filter `editable_roles` can be used to limit the roles
= 2.4.11 =
* Fix issue with running on PHP8.1
= 2.4.9 =
* Improve group-assignement UI
= 2.4.8 =
* Make textfields in settings-page wider
= 2.4.7 =
* Replace deprecated function
* Fix undefined index
* Add filter for retrieving other params at login (authLdap_filter_attributes)
* Add do_action after successfull login (authLdap_login_successful)
= 2.4.0 =
* Allow to use environment variables for LDAP-URI configuration
= 2.3.0 =
* Allow to not overwrite existing WordPress-Users with LDAP-Users as that can be a security issue.
= 2.1.0 =
* Add search-base for groups. This might come in handy for multisite-instances
= 2.0.0 =
* This new release adds Multi-Site support. It will no longer be possible to use this plugin just in one subsite of a multisite installation!
* Adds a warning screen to the config-section when no LDAPextension could be found
* Fixes an issue with the max-length of the username
= 1.5.1 =
* Fixes an issue with escaped backslashes and quotes
= 1.5.0 =
* Allows parts of the LDAP-URI to be URLEncoded
* Drops support for PHP 5.4
= 1.4.20 =
* Allows multiple LDAP-servers to be queried (given that they use the same attributes)
* Fixes issue with URL-Encoded informations (see https://github.com/heiglandreas/authLdap/issues/108)
= 1.4.19 =
* Adds support for TLS
= 1.4.14 =
* Update to showing password-fields check (thanks to @chaplina)
= 1.4.13 =
* Removed generation of default email-address (thanks to @henryk)
* Fixes password-hashing when caching passwords (thanks to @litinoveweedle)
* Removes the possibility to reset a password for LDAP-based users (thanks to @chaplina)
* Removes the password-change-Email from 4.3 on (thanks to @litinoveweedle)
* Fixes double authentication-attempt (that resulted in failed authentication) (thanks to @litinoveweedle)
= 1.4.10 =
* Cleanup by removing deprecated code
* Fixes issues with undefined variables
* Enables internal option-versioning
* Setting users nickname initially to the realname instead of the uid
* Fixes display of password-change possibility in users profile-page
= 1.4.9 =
* Fixed an issue with changing display name on every login
* Use proper way of looking up user-roles in setups w/o DB-prefix
= 1.4.8 =
* Updated version string
= 1.4.7 =
* Use default user to retrieve group menberships and not logging in user.
* return the UID from the LDAP instead of the value given by the user
* remove unnecessary checkbox
* Adds a testsuite
* Fixes PSR2 violations
[…]
= 1.2.1 =
* Fixed an issue with group-ids
* Moved the code to GitHub (https://github.com/heiglandreas/authLdap)
= 1.1.0 =
* Changed the login-process. Now users that are not allowed to login due to
missing group-memberships are not created within your blog as was the standard
until Version 1.0.3 - Thanks to alex@tayts.com
* Changed the default mail-address that is created when no mail-address can be
retrieved from the LDAP from me@example.com to $username@example.com so that
a new user can be created even though the mail address already exists in your
blog - Also thanks to alex@tayts.com
* Added support for WordPress-Table-prefixes as the capabilities of a user
are interlany stored in a field that is named "$tablePrefix_capabilities" -
again thanks to alex@tayts.com and also to sim0n of silicium.mine.nu

View File

@ -1,6 +0,0 @@
Contact: mailto://andreas@heigl.net
Contact: https://github.com/heiglandreas/authLdap/security/advisories/new
Expires: 2026-09-07T10:00:00.000Z
Encryption: https://andreas.heigl.org/publickey/
Encryption: https://heigl.org/.well-known/openpgpkey/hu/sfqdema7hgdj146cwzo4rxgsoujxis31
Preferred-Languages: en,de

View File

@ -1,24 +0,0 @@
<?php
/**
* Copyright Andrea Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap\Exception;
use Exception;
class Error extends Exception
{
public function __construct($message, $line = null)
{
parent::__construct($message);
if ($line) {
$this -> line = $line;
}
}
}

View File

@ -1,74 +0,0 @@
<?php
/**
* Copyright Andreas Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap\Exception;
use RuntimeException;
use function sprintf;
class InvalidLdapUri extends RuntimeException
{
public static function cannotparse(string $ldapUri): self
{
return new self(sprintf(
'%1$s seems not to be a valid URI',
$ldapUri
));
}
public static function wrongSchema(string $uri): self
{
return new self(sprintf(
'%1$s does not start with a valid schema',
$uri
));
}
public static function noSchema(string $uri): self
{
return new self(sprintf(
'%1$s does not provide a schema',
$uri
));
}
public static function noEnvironmentVariableSet(string $uri): self
{
return new self(sprintf(
'The environment variable %1$s does not provide a URI',
$uri
));
}
public static function noServerProvided(string $uri): self
{
return new self(sprintf(
'The LDAP-URI %1$s does not provide a server',
$uri
));
}
public static function noSearchBaseProvided(string $uri): self
{
return new self(sprintf(
'The LDAP-URI %1$s does not provide a search-base',
$uri
));
}
public static function invalidSearchBaseProvided(string $uri): self
{
return new self(sprintf(
'The LDAP-URI %1$s does not provide a valid search-base',
$uri
));
}
}

View File

@ -1,23 +0,0 @@
<?php
/**
* Copyright Andreas Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap\Exception;
use RuntimeException;
class MissingValidLdapConnection extends Error
{
public static function get(): self
{
return new self(sprintf(
'No valid LDAP connection available'
));
}
}

View File

@ -1,24 +0,0 @@
<?php
/**
* Copyright Andreas Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap\Exception;
use RuntimeException;
class SearchUnsuccessfull extends RuntimeException
{
public static function fromSearchFilter(string $filter): self
{
return new self(sprintf(
'Search for %1$s was not successfull',
$filter
));
}
}

View File

@ -1,93 +0,0 @@
<?php
/**
* Copyright (c) Andreas Heigl<andreas@heigl.org>
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author Andreas Heigl<andreas@heigl.org>
* @copyright Andreas Heigl
* @license http://www.opensource.org/licenses/mit-license.php MIT-License
* @since 07.07.2016
* @link http://github.com/heiglandreas/authLDAP
*/
namespace Org_Heigl\AuthLdap;
use Exception;
use Org_Heigl\AuthLdap\Exception\Error;
use Org_Heigl\AuthLdap\Exception\SearchUnsuccessfull;
use Org_Heigl\AuthLdap\Manager\Ldap;
class LdapList
{
/**
* @var Ldap[]
*/
protected $items = [];
public function addLdap(Ldap $ldap)
{
$this->items[] = $ldap;
}
public function authenticate($username, $password, $filter = '(uid=%s)')
{
/** @var Ldap $item */
foreach ($this->items as $key => $item) {
if (! $item->authenticate($username, $password, $filter)) {
unset($this->items[$key]);
continue;
}
return true;
}
return false;
}
public function bind()
{
$allFailed = true;
foreach ($this->items as $key => $item) {
try {
$item->bind();
} catch (\Exception $e) {
unset($this->items[$key]);
continue;
}
$allFailed = false;
}
if ($allFailed) {
throw new Error('No bind successfull');
}
return true;
}
public function search($filter, $attributes = array('uid'), $base = '')
{
foreach ($this->items as $item) {
try {
$result = $item->search($filter, $attributes, $base);
return $result;
} catch (Exception $e) {
}
}
throw SearchUnsuccessfull::fromSearchFilter($filter);
}
}

View File

@ -1,179 +0,0 @@
<?php
/**
* Copyright (c) Andreas Heigl<andreas@heigl.org>
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author Andreas Heigl<andreas@heigl.org>
* @copyright Andreas Heigl
* @license http://www.opensource.org/licenses/mit-license.php MIT-License
* @since 19.07.2020
* @link http://github.com/heiglandreas/authLDAP
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap;
use Org_Heigl\AuthLdap\Exception\InvalidLdapUri;
use function array_map;
use function error_get_last;
use function getenv;
use function is_array;
use function is_string;
use function parse_url;
use function preg_replace_callback;
use function rawurlencode;
use function strlen;
use function strpos;
use function substr;
use function trim;
use function urldecode;
final class LdapUri
{
private $server;
private $scheme;
private $port = 389;
private string $baseDn;
private $username = '';
private $password = '';
private function __construct(string $uri)
{
if (!preg_match('/^(ldap|ldaps|env)/', $uri)) {
throw InvalidLdapUri::wrongSchema($uri);
}
if (strpos($uri, 'env:') === 0) {
$newUri = getenv(substr($uri, 4));
if (false === $newUri) {
throw InvalidLdapUri::noEnvironmentVariableSet($uri);
}
$uri = (string) $newUri;
}
$uri = $this->injectEnvironmentVariables($uri);
$array = parse_url($uri);
if (!is_array($array)) {
throw InvalidLdapUri::cannotparse($uri);
}
$url = array_map(static function ($item) {
if (is_int($item)) {
return $item;
}
return urldecode($item);
}, $array);
if (!isset($url['scheme'])) {
throw InvalidLdapUri::noSchema($uri);
}
if (0 !== strpos($url['scheme'], 'ldap')) {
throw InvalidLdapUri::wrongSchema($uri);
}
if (!isset($url['host'])) {
throw InvalidLdapUri::noServerProvided($uri);
}
if (!isset($url['path'])) {
throw InvalidLdapUri::noSearchBaseProvided($uri);
}
if (1 === strlen($url['path'])) {
throw InvalidLdapUri::invalidSearchBaseProvided($uri);
}
$this->server = $url['host'];
$this->scheme = $url['scheme'];
$this->baseDn = substr($url['path'], 1);
if (isset($url['user'])) {
$this->username = $url['user'];
}
if ('' === trim($this->username)) {
$this->username = 'anonymous';
}
if (isset($url['pass'])) {
$this->password = $url['pass'];
}
if ($this->scheme === 'ldaps' && $this->port = 389) {
$this->port = 636;
}
// When someone sets the port in the URL we overwrite whatever is set.
// We have to assume they know what they are doing!
if (isset($url['port'])) {
$this->port = $url['port'];
}
}
public static function fromString(string $uri): LdapUri
{
return new LdapUri($uri);
}
private function injectEnvironmentVariables(string $base): string
{
return preg_replace_callback('/%env:([^%]+)%/', static function (array $matches) {
return rawurlencode(getenv($matches[1]));
}, $base);
}
public function toString(): string
{
return $this->scheme . '://' . $this->server . ':' . $this->port;
}
public function __toString()
{
return $this->toString();
}
public function getUsername(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
public function getBaseDn(): string
{
return $this->baseDn;
}
public function isAnonymous(): bool
{
if ($this->password === '') {
return true;
}
if ($this->username === 'anonymous') {
return true;
}
return false;
}
}

View File

@ -1,164 +0,0 @@
<?php
/**
* $Id: ldap.php 381646 2011-05-06 09:37:31Z heiglandreas $
*
* authLdap - Authenticate Wordpress against an LDAP-Backend.
* Copyright (c) 2008 Andreas Heigl<andreas@heigl.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* This file handles the basic LDAP-Tasks
*
* @author Andreas Heigl<andreas@heigl.org>
* @package authLdap
* @category authLdap
* @since 2008
*/
namespace Org_Heigl\AuthLdap\Manager;
use Org_Heigl\AuthLdap\Exception\Error;
use Org_Heigl\AuthLdap\Exception\MissingValidLdapConnection;
use Org_Heigl\AuthLdap\LdapUri;
use Org_Heigl\AuthLdap\Wrapper\LdapFactory;
use Org_Heigl\AuthLdap\Wrapper\LdapInterface;
class Ldap
{
/**
* This property contains the connection handle to the ldap-server
*
* @var LdapInterface|null
*/
private ?LdapInterface $connection;
private LdapUri $uri;
private LdapFactory $factory;
private $starttls;
public function __construct(LdapFactory $factory, LdapUri $uri, $starttls = false)
{
$this->starttls = $starttls;
$this->uri = $uri;
$this->factory = $factory;
$this->connection = null;
}
/**
* Connect to the given LDAP-Server
*/
public function connect(): self
{
$this->disconnect();
$this->connection = $this->factory->createFromLdapUri($this->uri->toString());
$this->connection->setOption(LDAP_OPT_PROTOCOL_VERSION, 3);
$this->connection->setOption(LDAP_OPT_REFERRALS, 0);
//if configured try to upgrade encryption to tls for ldap connections
if ($this->starttls) {
$this->connection->startTls();
}
return $this;
}
/**
* Disconnect from a resource if one is available
*/
public function disconnect(): self
{
if (null !== $this->connection) {
$this->connection->unbind();
}
$this->connection = null;
return $this;
}
/**
* Bind to an LDAP-Server with the given credentials
*
* @throws Error
*/
public function bind(): self
{
if (!$this->connection) {
$this->connect();
}
if (null === $this->connection) {
throw MissingValidLdapConnection::get();
}
if ($this->uri->isAnonymous()) {
$bind = $this->connection->bind();
} else {
$bind = $this->connection->bind($this->uri->getUsername(), $this->uri->getPassword());
}
if (!$bind) {
throw new Error('bind was not successfull: ' . $this->connection->error());
}
return $this;
}
/**
* This method does the actual ldap-serch.
*
* This is using the filter <var>$filter</var> for retrieving the attributes
* <var>$attributes</var>
*
* @return array<string|int, mixed>
* @throws Error
*/
public function search(string $filter, array $attributes = ['uid'], ?string $base = ''): array
{
if (null === $this->connection) {
throw new Error('No resource handle available');
}
if (!$base) {
$base = $this->uri->getBaseDn();
}
$result = $this->connection->search($base, $filter, $attributes);
if ($result === false) {
throw new Error('no result found');
}
$info = $this->connection->getEntries($result);
if ($info === false) {
throw new Error('invalid results found');
}
return $info;
}
/**
* This method authenticates the user <var>$username</var> using the
* password <var>$password</var>
*
* @param string $filter OPTIONAL This parameter defines the Filter to be used
* when searchin for the username. This MUST contain the string '%s' which
* will be replaced by the vaue given in <var>$username</var>
* @throws Error
*/
public function authenticate(string $username, string $password, string $filter = '(uid=%s)'): bool
{
$this->connect();
$this->bind();
$res = $this->search(sprintf($filter, $this->factory->escape($username, '', LDAP_ESCAPE_FILTER)));
if ($res ['count'] !== 1) {
return false;
}
$dn = $res[0]['dn'];
return $username && $password && $this->connection->bind($dn, $password);
}
}

View File

@ -1,54 +0,0 @@
<?php
/**
* Copyright Andreas Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap;
use WP_User;
use function array_search;
use function in_array;
use function var_dump;
class UserRoleHandler
{
/**
* @param WP_User $user
* @param string[] $roles
* @return void
*/
public function addRolesToUser(WP_User $user, $roles) : void
{
if ($roles === []) {
return;
}
if ($user->roles == $roles) {
return;
}
// Remove unused roles from existing.
foreach ($user->roles as $role) {
if (!in_array($role, $roles)) {
// Remove unused roles.
$user->remove_role($role);
continue;
}
// Remove the existing role from roles.
if (($key = array_search($role, $roles)) !== false) {
unset($roles[$key]);
}
}
// Add new ones if not already assigned.
foreach ($roles as $role) {
$user->add_role($role);
}
}
}

View File

@ -1,92 +0,0 @@
<?php
/**
* Copyright Andreas Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap\Wrapper;
use function ldap_bind;
use function ldap_connect;
use function ldap_error;
use function ldap_escape;
use function ldap_get_entries;
use function ldap_set_option;
use function ldap_start_tls;
use function ldap_unbind;
final class Ldap implements LdapInterface
{
private $connection;
public function __construct(string $ldapUri)
{
$this->connection = ldap_connect($ldapUri);
}
public function bind($dn = null, $password = null)
{
if (null === $dn && null === $password) {
return ldap_bind($this->connection);
}
return ldap_bind($this->connection, $dn, $password);
}
public function unbind()
{
return ldap_unbind($this->connection);
}
public function setOption($option, $value)
{
return ldap_set_option($this->connection, $option, $value);
}
public function startTls()
{
return ldap_start_tls($this->connection);
}
public function error()
{
return ldap_error($this->connection);
}
public function errno()
{
return ldap_errno($this->connection);
}
public function search(
$base,
$filter,
array $attributes = [],
$attributes_only = 0,
$sizelimit = -1,
$timelimit = -1
) {
return ldap_search(
$this->connection,
$base,
$filter,
$attributes,
$attributes_only,
$sizelimit,
$timelimit
);
}
public function getEntries($search_result)
{
return ldap_get_entries($this->connection, $search_result);
}
public static function escape(string $value, string $ignore = '', int $flags = 0): string
{
return ldap_escape($value, $ignore, $flags);
}
}

View File

@ -1,24 +0,0 @@
<?php
/**
* Copyright Andreas Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap\Wrapper;
class LdapFactory
{
public function createFromLdapUri(string $ldapUri): LdapInterface
{
return new Ldap($ldapUri);
}
public function escape($value, $ignore = '', $flags = 0): string
{
return Ldap::escape($value, $ignore, $flags);
}
}

View File

@ -1,39 +0,0 @@
<?php
/**
* Copyright Andreas Heigl <andreas@heigl.org>
*
* Licenses under the MIT-license. For details see the included file LICENSE.md
*/
declare(strict_types=1);
namespace Org_Heigl\AuthLdap\Wrapper;
interface LdapInterface
{
public function bind($dn = null, $password = null);
public function unbind();
public function setOption($option, $value);
public function startTls();
public function error();
public function errno();
public function search(
$base,
$filter,
array $attributes = [],
$attributes_only = 0,
$sizelimit = -1,
$timelimit = -1
);
public function getEntries($search_result);
public static function escape(string $value, string $ignore = '', int $flags = 0): string;
}

View File

@ -1,454 +0,0 @@
<?php
/**
* Copyright (c)2014-2014 heiglandreas
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIBILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @category
* @author Andreas Heigl<andreas@heigl.org>
* @copyright ©2014-2014 Andreas Heigl
* @license http://www.opesource.org/licenses/mit-license.php MIT-License
* @version 0.0
* @since 19.12.14
* @link https://github.com/heiglandreas/authLdap
*/
?><div class="wrap">
<?php if (! extension_loaded('ldap')) : ?>
<div class="error"><strong>Caveat:</strong> The LDAP-extension is not loaded!
Without that extension it is not possible to query an LDAP-Server! Please have a look
at <a href="http://php.net/manual/install.php">the PHP-Installation page</a>
</div>
<?php endif ?>
<h2>AuthLDAP Options</h2>
<form class="authldap-options" method="post" id="authLDAP_options" action="<?php echo $action;?>">
<input name="authLdapNonce" type="hidden" value="<?php echo wp_create_nonce('authLdapNonce'); ?>" />
<h3 class="title">General Usage of authLDAP</h3>
<fieldset class="options">
<table class="form-table">
<tr>
<th>
<label for="authLDAPAuth">Enable Authentication via LDAP?</label>
</th>
<td>
<input type="checkbox" name="authLDAPAuth" id="authLDAPAuth" value="1"<?php echo $tChecked; ?>/>
</td>
</tr>
<tr>
<th>
<label for="authLDAPDebug">Debug AuthLDAP?</label>
</th>
<td>
<input type="checkbox" name="authLDAPDebug" id="authLDAPDebug" value="1"<?php echo $tDebugChecked; ?>/>
</td>
</tr>
<tr>
<th>
<label for="authLDAPDoNotOverwriteNonLdapUsers">Do not authenticate existing WordPress-Users</label>
</th>
<td>
<input type="checkbox" name="authLDAPDoNotOverwriteNonLdapUsers" id="authLDAPDoNotOverwriteNonLdapUsers" value="1"<?php echo $tDoNotOverwriteNonLdapUsers; ?>/>
<p class="description">
Shall we prohibit authenticating already in WordPress created users using LDAP? If you enable this, LDAP-Users with the same user-ID
as existing WordPress-Users can no longer take over the WordPress-Users account. This also means that LDAP-Users with the same User-ID as existing
WordPress-Users will <strong>not</strong> be able to authenticate anymore! Accounts that have been taken over already will not be affected by this setting.
</p>
<p class="description">This should only be checked if you know what you are doing!</p>
</td>
</tr>
<tr>
<th>
<label for="authLDAPCachePW">Save entered passwords in the wordpress user table?</label>
</th>
<td>
<input type="checkbox" name="authLDAPCachePW" id="authLDAPCachePW" value="1"<?php echo $tPWChecked; ?>/>
</td>
</tr>
<tr>
<th>
<label for="authLDAPGroupEnable">Map LDAP Groups to wordpress Roles?</label>
</th>
<td>
<input type="checkbox" name="authLDAPGroupEnable" id="authLDAPGroupEnable" value="1"<?php echo $tGroupChecked; ?>/>
<p class="description">
Search LDAP for user's groups and map to Wordpress Roles.
</p>
</td>
</tr>
</table>
</fieldset>
<h3 class="title">General Server Settings</h3>
<fieldset class="options">
<table class="form-table">
<tr>
<th>
<label for="authLDAPURI">LDAP URI</label>
</th>
<td>
<input type="text" name="authLDAPURI" id="authLDAPURI" placeholder="LDAP-URI"
class="regular-text" value="<?php echo $authLDAPURI; ?>"/>
<p class="description">
The <abbr title="Uniform Ressource Identifier">URI</abbr>
for connecting to the LDAP-Server. This usualy takes the form
<var>&lt;scheme&gt;://&lt;user&gt;:&lt;password&gt;@&lt;server&gt;/&lt;path&gt;</var>
according to RFC 1738.</p>
<p class="description">
In this case it schould be something like
<var>ldap://uid=adminuser,dc=example,c=com:secret@ldap.example.com/dc=basePath,dc=example,c=com</var>.
</p>
<p class="description">
If your LDAP accepts anonymous login, you can ommit the user and
password-Part of the URI
</p>
<p class="description">
You can use the pseudo-schema <em>env</em> to provide your LDAP-URI from an environment-variable. So if you have your
LDAP-URI in a variable called <code>LDAP_URI</code> you can enter <code>env:LDAP_URI</code> in this field and at runtime the
appropriate value will be taken from the Environment-variable <code>LDAP_URI</code>. If the varialbe is not set, then the value will be empty.
</p>
<p class="description">
You can also provide different parts of the LDP-URI from environment variables by providing
<code>%env:[VARIABLENAME]%</code> within your LDAP-URI. So if you want to provide the
password from an Environment-variable <code>LDAP_PASSWORD</code> your LDAP-URI looks like
<code>ldap://uid=adminuser,dc=example,c=com:%env:LDAP_PASSWORD%@ldap.example.com/dc=basePath,dc=example,c=com</code>
</p>
<p class="description">
<strong>Caveat!</strong><br/>
If you are using Environment-variables for parts of the LDAP-URL then those <strong>must not</strong> be URL-Encoded!<br/>
Otherwise the different parts <strong>must</strong> be URL-Encoded!
</p>
</td>
</tr>
<tr>
<th>
<label for="authLDAPURISeparator">LDAP URI-Separator</label>
</th>
<td>
<input type="text" name="authLDAPURISeparator" id="authLDAPURISeparator" placeholder="LDAP-URI Separator"
class="regular-text" value="<?php echo $authLDAPURISeparator; ?>"/>
<p class="description">
A separator that separates multiple LDAP-URIs from one another.
You can use that feature to try to authenticate against multiple LDAP-Servers
as long as they all have the same attribute-settings. The first LDAP-Server the user can
authenticate against will be used to handle the user.
</td>
</tr>
<tr>
<th>
<label for="authLDAPStartTLS" class="description">StartTLS</label>
</th>
<td>
<input type="checkbox" name="authLDAPStartTLS" id="authLDAPStartTLS" value="1"<?php echo $tStartTLSChecked; ?>/>
<p class="description">
Use StartTLS for encryption of ldap connections. This setting is not to be used in combination with ldaps connections (ldap:// only).
</p>
</td>
<tr>
<th scope="row">
<label for="authLDAPFilter" class="description">Filter</label>
</th>
<td>
<input type="text" name="authLDAPFilter" id="authLDAPFilter" placeholder="(uid=%s)"
class="regular-text" value="<?php echo $authLDAPFilter; ?>"/>
<p class="description">
Please provide a valid filter that can be used for querying the
<abbr title="Lightweight Directory Access Protocol">LDAP</abbr>
for the correct user. For more information on this
feature have a look at <a href="http://andreas.heigl.org/cat/dev/wp/authldap">http://andreas.heigl.org/cat/dev/wp/authldap</a>
</p>
<p class="description">
This field <strong>should</strong> include the string <code>%s</code>
that will be replaced with the username provided during log-in
</p>
<p class="description">
If you leave this field empty it defaults to <strong>(uid=%s)</strong>
</p>
</td>
</tr>
</table>
</fieldset>
<h3 class="title">Settings for creating new Users</h3>
<fieldset class="options">
<table class="form-table">
<tr>
<th scope="row">
<label for="authLDAPUseUserAccount">User-Read</label>
</th>
<td>
<input type="checkbox" name="authLDAPUseUserAccount" id="authLDAPUseUserAccount" value="1"<?php echo $tUserRead; ?>/><br />
<p class="description">
If checked the plugin will use the user's account to query their own information. If not it will use the admin account.
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPNameAttr">Name-Attribute</label>
</th>
<td>
<input type="text" name="authLDAPNameAttr" id="authLDAPNameAttr" placeholder="name"
class="regular-text" value="<?php echo $authLDAPNameAttr; ?>"/><br />
<p class="description">
Which Attribute from the LDAP contains the Full or the First name
of the user trying to log in.
</p>
<p class="description">
This defaults to <strong>name</strong>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPSecName">Second Name Attribute</label>
</th>
<td>
<input type="text" name="authLDAPSecName" id="authLDAPSecName" placeholder=""
class="regular-text" value="<?php echo $authLDAPSecName; ?>" />
<p class="description">
If the above Name-Attribute only contains the First Name of the
user you can here specify an Attribute that contains the second name.
</p>
<p class="description">
This field is empty by default
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPUidAttr">User-ID Attribute</label>
</th>
<td>
<input type="text" name="authLDAPUidAttr" id="authLDAPUidAttr" placeholder="uid"
class="regular-text" value="<?php echo $authLDAPUidAttr; ?>" />
<p class="description">
Please give the Attribute, that is used to identify the user. This
should be the same as you used in the above <em>Filter</em>-Option
</p>
<p class="description">
This field defaults to <strong>uid</strong>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPMailAttr">Mail Attribute</label>
</th>
<td>
<input type="text" name="authLDAPMailAttr" id="authLDAPMailAttr" placeholder="mail"
class="regular-text" value="<?php echo $authLDAPMailAttr; ?>" />
<p class="description">
Which Attribute holds the eMail-Address of the user?
</p>
<p class="description">
If more than one eMail-Address are stored in the LDAP, only the first given is used
</p>
<p class="description">
This field defaults to <strong>mail</strong>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPWebAttr">Web-Attribute</label>
</th>
<td>
<input type="text" name="authLDAPWebAttr" id="authLDAPWebAttr" placeholder=""
class="regular-text" value="<?php echo $authLDAPWebAttr; ?>" />
<p class="description">
If your users have a personal page (URI) stored in the LDAP, it can
be provided here.
</p>
<p class="description">
This field is empty by default
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPDefaultRole">Default Role</label>
</th>
<td>
<select name="authLDAPDefaultRole" id="authLDAPDefaultRole">
<option value="" <?php echo ( $authLDAPDefaultRole == '' ? 'selected="selected"' : '' ); ?>>
None (deny access)
</option>
<?php foreach ($roles->get_names() as $group => $vals) : ?>
<option value="<?php echo $group; ?>" <?php echo ( $authLDAPDefaultRole == $group ? 'selected="selected"' : '' ); ?>>
<?php echo $vals; ?>
</option>
<?php endforeach; ?>
</select>
<p class="description">
Here you can select the default role for users.
If you enable LDAP Groups below, they will take precedence over the Default Role.
</p>
<p class="description">
Existing users will retain their roles unless overriden by LDAP Groups below.
</p>
</td>
</tr>
</table>
</fieldset>
<div id="authldaprolemapping">
<h3 class="title">Groups for Roles</h3>
<fieldset class="options">
<table class="form-table">
<tr>
<th>
<label for="authLDAPGroupOverUser">LDAP Groups override role of existing users?</label>
</th>
<td>
<input type="checkbox" name="authLDAPGroupOverUser" id="authLDAPGroupOverUser" value="1"<?php echo $tGroupOverUserChecked; ?>/>
<p class="description">
If role determined by LDAP Group differs from existing Wordpress User's role, use LDAP Group.
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPGroupBase">Group-Base</label>
</th>
<td>
<input type="text" name="authLDAPGroupBase" id="authLDAPGroupBase" placeholder=""
class="regular-text" value="<?php echo $authLDAPGroupBase; ?>" />
<p class="description">
This is the base dn to lookup groups.
</p>
<p class="description">
If empty the base dn of the LDAP URI will be used
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPGroupAttr">Group-Attribute</label>
</th>
<td>
<input type="text" name="authLDAPGroupAttr" id="authLDAPGroupAttr" placeholder="gidNumber"
class="regular-text" value="<?php echo $authLDAPGroupAttr; ?>" />
<p class="description">
This is the attribute that defines the Group-ID that can be matched
against the Groups defined further down
</p>
<p class="description">
This field defaults to <strong>gidNumber</strong>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPGroupSeparator">Group-Separator</label>
</th>
<td>
<input type="text" name="authLDAPGroupSeparator" id="authLDAPGroupSeparator" placeholder=","
class="regular-text" value="<?php echo $authLDAPGroupSeparator; ?>" />
<p class="description">
This attribute defines the separator used for the Group-IDs listed in the
Groups defined further down. This is useful if the value of Group-Attribute
listed above can contain a comma (for example, when using the memberof attribute)
</p>
<p class="description">
This field defaults to <strong>, (comma)</strong>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="authLDAPGroupFilter">Group-Filter</label>
</th>
<td>
<input type="text" name="authLDAPGroupFilter" id="authLDAPGroupFilter"
placeholder="(&amp;(objectClass=posixGroup)(memberUid=%s))"
class="regular-text" value="<?php echo $authLDAPGroupFilter; ?>" />
<p class="description">
Here you can add the filter for selecting groups for ther
currentlly logged in user
</p>
<p class="description">
The Filter should contain the string <code>%s</code> which will be replaced by
the login-name of the currently logged in user
</p>
<p class="description">
Alternatively the string <code>%dn%</code> will be replaced by the
DN of the currently logged in user. This can be helpfull if
group-memberships are defined with DNs rather than UIDs
</p>
<p class="description">This field defaults to
<strong>(&amp;(objectClass=posixGroup)(memberUid=%s))</strong>
</p>
</td>
</tr>
</table>
</fieldset>
<h3 class="title">Role - group mapping</h3>
<fieldset class="options">
<p class="description">You can set multiple values per role by separating them with a coma</p>
<p class="description">The values are empty by default</p>
<table class="form-table">
<thead>
<th scope="row">Assign this WordPress-Role</th>
<th style="width:auto;">to members of this/these LDAP-Groups</th>
</thead>
<tbody>
<?php
foreach ($roles->get_names() as $group => $vals) :
$aGroup=$authLDAPGroups[$group]; ?>
<tr>
<th scope="row" style="width:auto; min-width: 200px;">
<label for="authLDAPGroups[<?php echo $group; ?>]">
<?php echo $vals; ?>
</label>
</th>
<td>
<input type="text" name="authLDAPGroups[<?php echo $group; ?>]" id="authLDAPGroups[<?php echo $group; ?>]"
value="<?php echo $aGroup; ?>" />
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</fieldset>
</div>
<fieldset class="buttons">
<p class="submit">
<input type="submit" name="ldapOptionsSave" class="button button-primary" value="Save Changes" />
</p>
</fieldset>
</form>
</div>
<script type="text/javascript">
elem = document.getElementById('authLDAPGroupEnable');
if(! elem.checked) {
document.getElementById('authldaprolemapping').setAttribute('style', 'display:none;');
}
elem.addEventListener('change', function(e){
if(! e.target.checked) {
document.getElementById('authldaprolemapping').setAttribute('style', 'display:none;');
} else {
document.getElementById('authldaprolemapping').removeAttribute('style');
}
});
</script>

View File

@ -62,7 +62,7 @@ function cau_incompatiblePlugins() {
$return = false;
foreach ( cau_incompatiblePluginlist() as $key => $value ) {
if( is_plugin_active( $key ) ) {
if( function_exists( 'is_plugin_active' ) && is_plugin_active( $key ) ) {
$return = true;
}
}
@ -97,7 +97,7 @@ function cau_pluginIssueCount() {
// cau_incompatiblePlugins
if( cau_incompatiblePlugins() ) {
foreach ( cau_incompatiblePluginlist() as $key => $value ) {
if( is_plugin_active( $key ) ) {
if( function_exists( 'is_plugin_active' ) && is_plugin_active( $key ) ) {
$count++;
}
}
@ -276,7 +276,6 @@ function cau_run_custom_hooks_c() {
}
if( $fileDate >= $lastday ) {
array_push( $allDates, $fileDate );
$status = ( $fileTime > $range_start && $fileTime < $range_end ) ? __( 'Automatic', 'companion-auto-update' ) : __( 'Manual', 'companion-auto-update' );
$totalNum++;
cau_updatePluginInformation( 'core', $status );
@ -717,10 +716,10 @@ function cau_list_outdated() {
$api = plugins_api( 'plugin_information', array( 'slug' => wp_unslash( $actualSlug ), 'tested' => true ) );
// Version compare
$tested_version = substr( $api->tested, 0, 3 ); // Format version number
$tested_version = !empty( $api->tested ) ? substr( $api->tested, 0, 3 ) : false; // Format version number
// Check if "tested up to" version number is set
if( $tested_version != '' ) {
if( $tested_version ) {
$current_version = substr( get_bloginfo( 'version' ), 0, 3 ); // Format version number
$version_difference = ( (int)$current_version - (int)$tested_version ); // Get the difference

View File

@ -4,7 +4,7 @@
* Plugin Name: Companion Auto Update
* Plugin URI: http://codeermeneer.nl/portfolio/companion-auto-update/
* Description: This plugin auto updates all plugins, all themes and the wordpress core.
* Version: 3.8.8
* Version: 3.9.0
* Author: Papin Schipper
* Author URI: http://codeermeneer.nl/
* Contributors: papin

View File

@ -5,7 +5,7 @@ Tags: auto, automatic, background, update, updates, updating, automatic updates,
Requires at least: 5.3.0
Tested up to: 6.4
Requires PHP: 5.1
Stable tag: 3.8.8
Stable tag: 3.9.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@ -82,6 +82,13 @@ So obviously, some of you wondered what the difference would be between the defa
== Changelog ==
= 3.9.0 (February 2, 2024) =
* Fixed: Fatal error during Cron
* Fixed: PHP deprecated error for PHP 8 and up
= 3.8.9 (January 9, 2024) =
* Fixed error: Call to undefined function is_plugin_active()
= 3.8.8 (December 19, 2023) =
* Fixed a few minor bugs
* Made some performance improvements

View File

@ -1,134 +0,0 @@
<?php
/**
* This file creates a class to build our CSS.
*
* @package GP Premium
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // No direct access, please.
}
if ( ! class_exists( 'GeneratePress_Backgrounds_CSS' ) ) {
/**
* Generate our background CSS.
*/
class GeneratePress_Backgrounds_CSS {
/**
* The css selector that you're currently adding rules to
*
* @access protected
* @var string
*/
protected $_selector = ''; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* Stores the final css output with all of its rules for the current selector.
*
* @access protected
* @var string
*/
protected $_selector_output = ''; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* Stores all of the rules that will be added to the selector
*
* @access protected
* @var string
*/
protected $_css = ''; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* The string that holds all of the css to output
*
* @access protected
* @var string
*/
protected $_output = ''; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
/**
* Sets a selector to the object and changes the current selector to a new one
*
* @access public
* @since 1.0
*
* @param string $selector - the css identifier of the html that you wish to target.
* @return $this
*/
public function set_selector( $selector = '' ) {
// Render the css in the output string everytime the selector changes.
if ( '' !== $this->_selector ) {
$this->add_selector_rules_to_output();
}
$this->_selector = $selector;
return $this;
}
/**
* Adds a css property with value to the css output
*
* @access public
* @since 1.0
*
* @param string $property - the css property.
* @param string $value - the value to be placed with the property.
* @param string $url Whether we need to generate URL in the string.
* @return $this
*/
public function add_property( $property, $value, $url = '' ) {
// If we don't have a value or our value is the same as our og default, bail.
if ( empty( $value ) ) {
return false;
}
// Set up our background image URL param if needed.
$url_start = ( '' !== $url ) ? "url('" : ""; // phpcs:ignore -- need double quotes.
$url_end = ( '' !== $url ) ? "')" : ""; // phpcs:ignore -- need double quotes.
$this->_css .= $property . ':' . $url_start . $value . $url_end . ';';
return $this;
}
/**
* Adds the current selector rules to the output variable
*
* @access private
* @since 1.0
*
* @return $this
*/
private function add_selector_rules_to_output() {
if ( ! empty( $this->_css ) ) {
$this->_selector_output = $this->_selector;
$selector_output = sprintf( '%1$s{%2$s}', $this->_selector_output, $this->_css );
$this->_output .= $selector_output;
// Reset the css.
$this->_css = '';
}
return $this;
}
/**
* Returns the minified css in the $_output variable
*
* @access public
* @since 1.0
*
* @return string
*/
public function css_output() {
// Add current selector's rules to output.
$this->add_selector_rules_to_output();
// Output minified css.
return $this->_output;
}
}
}

View File

@ -1,420 +0,0 @@
<?php
/**
* This file handles Secondary Nav background images.
*
* @package GP Premium
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // No direct access, please.
}
if ( ! function_exists( 'generate_backgrounds_secondary_nav_customizer' ) ) {
add_action( 'customize_register', 'generate_backgrounds_secondary_nav_customizer', 1000 );
/**
* Adds our Secondary Nav background image options
*
* These options are in their own function so we can hook it in late to
* make sure Secondary Nav is activated.
*
* 1000 priority is there to make sure Secondary Nav is registered (999)
* as we check to see if the layout control exists.
*
* Secondary Nav now uses 100 as a priority.
*
* @param object $wp_customize Our Customizer object.
*/
function generate_backgrounds_secondary_nav_customizer( $wp_customize ) {
if ( ! function_exists( 'generate_secondary_nav_get_defaults' ) ) {
return;
}
if ( ! $wp_customize->get_section( 'secondary_nav_section' ) ) {
return;
}
$defaults = generate_secondary_nav_get_defaults();
if ( method_exists( $wp_customize, 'register_control_type' ) ) {
$wp_customize->register_control_type( 'GeneratePress_Section_Shortcut_Control' );
}
require_once GP_LIBRARY_DIRECTORY . 'customizer-helpers.php';
$wp_customize->add_section(
'secondary_bg_images_section',
array(
'title' => __( 'Secondary Navigation', 'gp-premium' ),
'capability' => 'edit_theme_options',
'description' => '',
'panel' => 'generate_backgrounds_panel',
'priority' => 21,
)
);
$wp_customize->add_control(
new GeneratePress_Section_Shortcut_Control(
$wp_customize,
'generate_secondary_navigation_background_image_shortcuts',
array(
'section' => 'secondary_bg_images_section',
'element' => __( 'Secondary Navigation', 'gp-premium' ),
'shortcuts' => array(
'layout' => 'secondary_nav_section',
'colors' => 'secondary_navigation_color_section',
'typography' => 'secondary_font_section',
),
'settings' => ( isset( $wp_customize->selective_refresh ) ) ? array() : 'blogname',
'priority' => 1,
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_image]',
array(
'default' => $defaults['nav_image'],
'type' => 'option',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
'generate_secondary_backgrounds-nav-image',
array(
'section' => 'secondary_bg_images_section',
'settings' => 'generate_secondary_nav_settings[nav_image]',
'priority' => 750,
'label' => __( 'Navigation', 'gp-premium' ),
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_repeat]',
array(
'default' => $defaults['nav_repeat'],
'type' => 'option',
'sanitize_callback' => 'generate_premium_sanitize_choices',
)
);
$wp_customize->add_control(
'generate_secondary_nav_settings[nav_repeat]',
array(
'type' => 'select',
'section' => 'secondary_bg_images_section',
'choices' => array(
'' => __( 'Repeat', 'gp-premium' ),
'repeat-x' => __( 'Repeat x', 'gp-premium' ),
'repeat-y' => __( 'Repeat y', 'gp-premium' ),
'no-repeat' => __( 'No Repeat', 'gp-premium' ),
),
'settings' => 'generate_secondary_nav_settings[nav_repeat]',
'priority' => 800,
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_item_image]',
array(
'default' => $defaults['nav_item_image'],
'type' => 'option',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
'generate_secondary_backgrounds-nav-item-image',
array(
'section' => 'secondary_bg_images_section',
'settings' => 'generate_secondary_nav_settings[nav_item_image]',
'priority' => 950,
'label' => __( 'Navigation Item', 'gp-premium' ),
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_item_repeat]',
array(
'default' => $defaults['nav_item_repeat'],
'type' => 'option',
'sanitize_callback' => 'generate_premium_sanitize_choices',
)
);
$wp_customize->add_control(
'generate_secondary_nav_settings[nav_item_repeat]',
array(
'type' => 'select',
'section' => 'secondary_bg_images_section',
'choices' => array(
'' => __( 'Repeat', 'gp-premium' ),
'repeat-x' => __( 'Repeat x', 'gp-premium' ),
'repeat-y' => __( 'Repeat y', 'gp-premium' ),
'no-repeat' => __( 'No Repeat', 'gp-premium' ),
),
'settings' => 'generate_secondary_nav_settings[nav_item_repeat]',
'priority' => 1000,
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_item_hover_image]',
array(
'default' => $defaults['nav_item_hover_image'],
'type' => 'option',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
'generate_secondary_backgrounds-nav-item-hover-image',
array(
'section' => 'secondary_bg_images_section',
'settings' => 'generate_secondary_nav_settings[nav_item_hover_image]',
'priority' => 1150,
'label' => __( 'Navigation Item Hover', 'gp-premium' ),
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_item_hover_repeat]',
array(
'default' => $defaults['nav_item_hover_repeat'],
'type' => 'option',
'sanitize_callback' => 'generate_premium_sanitize_choices',
)
);
$wp_customize->add_control(
'generate_secondary_nav_settings[nav_item_hover_repeat]',
array(
'type' => 'select',
'section' => 'secondary_bg_images_section',
'choices' => array(
'' => __( 'Repeat', 'gp-premium' ),
'repeat-x' => __( 'Repeat x', 'gp-premium' ),
'repeat-y' => __( 'Repeat y', 'gp-premium' ),
'no-repeat' => __( 'No Repeat', 'gp-premium' ),
),
'settings' => 'generate_secondary_nav_settings[nav_item_hover_repeat]',
'priority' => 1200,
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_item_current_image]',
array(
'default' => $defaults['nav_item_current_image'],
'type' => 'option',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
'generate_secondary_backgrounds-nav-item-current-image',
array(
'section' => 'secondary_bg_images_section',
'settings' => 'generate_secondary_nav_settings[nav_item_current_image]',
'priority' => 1350,
'label' => __( 'Navigation Item Current', 'gp-premium' ),
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[nav_item_current_repeat]',
array(
'default' => $defaults['nav_item_current_repeat'],
'type' => 'option',
'sanitize_callback' => 'generate_premium_sanitize_choices',
)
);
$wp_customize->add_control(
'generate_secondary_nav_settings[nav_item_current_repeat]',
array(
'type' => 'select',
'section' => 'secondary_bg_images_section',
'choices' => array(
'' => __( 'Repeat', 'gp-premium' ),
'repeat-x' => __( 'Repeat x', 'gp-premium' ),
'repeat-y' => __( 'Repeat y', 'gp-premium' ),
'no-repeat' => __( 'No Repeat', 'gp-premium' ),
),
'settings' => 'generate_secondary_nav_settings[nav_item_current_repeat]',
'priority' => 1400,
)
);
$wp_customize->add_section(
'secondary_subnav_bg_images_section',
array(
'title' => __( 'Secondary Sub-Navigation', 'gp-premium' ),
'capability' => 'edit_theme_options',
'description' => '',
'panel' => 'generate_backgrounds_panel',
'priority' => 22,
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[sub_nav_item_image]',
array(
'default' => $defaults['sub_nav_item_image'],
'type' => 'option',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
'generate_secondary_backgrounds-sub-nav-item-image',
array(
'section' => 'secondary_subnav_bg_images_section',
'settings' => 'generate_secondary_nav_settings[sub_nav_item_image]',
'priority' => 1700,
'label' => __( 'Sub-Navigation Item', 'gp-premium' ),
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[sub_nav_item_repeat]',
array(
'default' => $defaults['sub_nav_item_repeat'],
'type' => 'option',
'sanitize_callback' => 'generate_premium_sanitize_choices',
)
);
$wp_customize->add_control(
'generate_secondary_nav_settings[sub_nav_item_repeat]',
array(
'type' => 'select',
'section' => 'secondary_subnav_bg_images_section',
'choices' => array(
'' => __( 'Repeat', 'gp-premium' ),
'repeat-x' => __( 'Repeat x', 'gp-premium' ),
'repeat-y' => __( 'Repeat y', 'gp-premium' ),
'no-repeat' => __( 'No Repeat', 'gp-premium' ),
),
'settings' => 'generate_secondary_nav_settings[sub_nav_item_repeat]',
'priority' => 1800,
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[sub_nav_item_hover_image]',
array(
'default' => $defaults['sub_nav_item_hover_image'],
'type' => 'option',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
'generate_secondary_backgrounds-sub-nav-item-hover-image',
array(
'section' => 'secondary_subnav_bg_images_section',
'settings' => 'generate_secondary_nav_settings[sub_nav_item_hover_image]',
'priority' => 2000,
'label' => __( 'Sub-Navigation Item Hover', 'gp-premium' ),
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[sub_nav_item_hover_repeat]',
array(
'default' => $defaults['sub_nav_item_hover_repeat'],
'type' => 'option',
'sanitize_callback' => 'generate_premium_sanitize_choices',
)
);
$wp_customize->add_control(
'generate_secondary_nav_settings[sub_nav_item_hover_repeat]',
array(
'type' => 'select',
'section' => 'secondary_subnav_bg_images_section',
'choices' => array(
'' => __( 'Repeat', 'gp-premium' ),
'repeat-x' => __( 'Repeat x', 'gp-premium' ),
'repeat-y' => __( 'Repeat y', 'gp-premium' ),
'no-repeat' => __( 'No Repeat', 'gp-premium' ),
),
'settings' => 'generate_secondary_nav_settings[sub_nav_item_hover_repeat]',
'priority' => 2100,
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[sub_nav_item_current_image]',
array(
'default' => $defaults['sub_nav_item_current_image'],
'type' => 'option',
'capability' => 'edit_theme_options',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
new WP_Customize_Image_Control(
$wp_customize,
'generate_secondary_backgrounds-sub-nav-item-current-image',
array(
'section' => 'secondary_subnav_bg_images_section',
'settings' => 'generate_secondary_nav_settings[sub_nav_item_current_image]',
'priority' => 2300,
'label' => __( 'Sub-Navigation Item Current', 'gp-premium' ),
)
)
);
$wp_customize->add_setting(
'generate_secondary_nav_settings[sub_nav_item_current_repeat]',
array(
'default' => $defaults['sub_nav_item_current_repeat'],
'type' => 'option',
'sanitize_callback' => 'generate_premium_sanitize_choices',
)
);
$wp_customize->add_control(
'generate_secondary_nav_settings[sub_nav_item_current_repeat]',
array(
'type' => 'select',
'section' => 'secondary_subnav_bg_images_section',
'choices' => array(
'' => __( 'Repeat', 'gp-premium' ),
'repeat-x' => __( 'Repeat x', 'gp-premium' ),
'repeat-y' => __( 'Repeat y', 'gp-premium' ),
'no-repeat' => __( 'No Repeat', 'gp-premium' ),
),
'settings' => 'generate_secondary_nav_settings[sub_nav_item_current_repeat]',
'priority' => 2400,
)
);
}
}

View File

@ -1,19 +0,0 @@
<?php
/**
* Backgrounds module.
*
* @since 1.1.0
*
* @package GP Premium
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // No direct access, please.
}
// Define the version. This used to be a standalone plugin, so we need to keep this constant.
if ( ! defined( 'GENERATE_BACKGROUNDS_VERSION' ) ) {
define( 'GENERATE_BACKGROUNDS_VERSION', GP_PREMIUM_VERSION );
}
require plugin_dir_path( __FILE__ ) . 'functions/functions.php';

View File

@ -1,169 +0,0 @@
<?php
/**
* This file handles column-related functionality.
*
* @package GP Premium
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // No direct access, please.
}
if ( ! function_exists( 'generate_blog_get_columns' ) ) {
/**
* Initiate columns.
*
* @since 0.1
*/
function generate_blog_get_columns() {
$generate_blog_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
// If columns are enabled, set to true.
$columns = ( $generate_blog_settings['column_layout'] ) ? true : false;
// If we're not dealing with posts, set it to false.
// Check for is_home() to prevent bug in Yoast that throws off the post type check.
$columns = ( 'post' === get_post_type() || is_search() || is_home() ) ? $columns : false;
// If masonry is enabled via filter, enable columns.
// phpcs:ignore -- Non-strict comparison allowed.
$columns = ( 'true' == apply_filters( 'generate_blog_masonry', 'false' ) ) ? true : $columns;
// If we're on a singular post or page, disable.
$columns = ( is_singular() ) ? false : $columns;
// Turn off columns if we're on a WooCommerce search page.
if ( function_exists( 'is_woocommerce' ) ) {
$columns = ( is_woocommerce() && is_search() ) ? false : $columns;
}
// Bail if there's no search results.
if ( is_search() ) {
global $wp_query;
// phpcs:ignore -- non-strict comparison allowed.
if ( 0 == $wp_query->post_count ) {
$columns = false;
}
}
// Return the result.
return apply_filters( 'generate_blog_columns', $columns );
}
}
if ( ! function_exists( 'generate_blog_get_masonry' ) ) {
/**
* Check if masonry is enabled.
* This function is a mess with strings as bools etc.. Will re-write in a big upate to get lots of testing.
*/
function generate_blog_get_masonry() {
$generate_blog_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
// If masonry is enabled via option or filter, enable it.
// phpcs:ignore -- non-strict comparison allowed.
if ( $generate_blog_settings['masonry'] || 'true' == apply_filters( 'generate_blog_masonry', 'false' ) ) {
$masonry = 'true';
} else {
$masonry = 'false';
}
// Allow masonry to be turned off using a boolean.
if ( false === apply_filters( 'generate_blog_masonry', 'false' ) ) {
$masonry = 'false';
}
return $masonry;
}
}
if ( ! function_exists( 'generate_blog_add_columns_container' ) ) {
add_action( 'generate_before_main_content', 'generate_blog_add_columns_container' );
/**
* Add columns container
*
* @since 1.0
*/
function generate_blog_add_columns_container() {
if ( ! generate_blog_get_columns() ) {
return;
}
$columns = generate_blog_get_column_count();
printf(
'<div class="generate-columns-container %1$s">%2$s',
'false' !== generate_blog_get_masonry() ? 'masonry-container are-images-unloaded' : '',
'false' !== generate_blog_get_masonry() ? '<div class="grid-sizer grid-' . esc_attr( $columns ) . ' tablet-grid-50 mobile-grid-100"></div>' : '' // phpcs:ignore -- no escaping needed.
);
}
}
if ( ! function_exists( 'generate_blog_add_ending_columns_container' ) ) {
add_action( 'generate_after_main_content', 'generate_blog_add_ending_columns_container' );
/**
* Add closing columns container
*
* @since 1.0
*/
function generate_blog_add_ending_columns_container() {
if ( ! generate_blog_get_columns() ) {
return;
}
echo '</div><!-- .generate-columns-contaier -->';
}
}
if ( ! function_exists( 'generate_blog_columns_css' ) ) {
/**
* Add inline CSS
*/
function generate_blog_columns_css() {
$generate_blog_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( function_exists( 'generate_spacing_get_defaults' ) ) {
$spacing_settings = wp_parse_args(
get_option( 'generate_spacing_settings', array() ),
generate_spacing_get_defaults()
);
}
$separator = ( function_exists( 'generate_spacing_get_defaults' ) ) ? absint( $spacing_settings['separator'] ) : 20;
$return = '';
if ( generate_blog_get_columns() ) {
$return .= '.generate-columns {margin-bottom: ' . $separator . 'px;padding-left: ' . $separator . 'px;}';
$return .= '.generate-columns-container {margin-left: -' . $separator . 'px;}';
$return .= '.page-header {margin-bottom: ' . $separator . 'px;margin-left: ' . $separator . 'px}';
$return .= '.generate-columns-container > .paging-navigation {margin-left: ' . $separator . 'px;}';
}
return $return;
}
}
if ( ! function_exists( 'generate_blog_get_column_count' ) ) {
/**
* Get our column grid class
*/
function generate_blog_get_column_count() {
$generate_blog_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
$count = $generate_blog_settings['columns'];
return apply_filters( 'generate_blog_get_column_count', $count );
}
}

View File

@ -1,149 +0,0 @@
.masonry-enabled .page-header {
position: relative !important;
}
.separate-containers .site-main > .generate-columns-container {
margin-bottom: 0;
}
.masonry-container.are-images-unloaded,
.load-more.are-images-unloaded,
.masonry-enabled #nav-below {
opacity: 0;
}
/* columns */
.generate-columns-container:not(.masonry-container) {
display: flex;
flex-wrap: wrap;
align-items: stretch;
}
.generate-columns .inside-article {
height: 100%;
box-sizing: border-box;
}
.generate-columns-activated.post-image-aligned-left .generate-columns-container article:not(.featured-column) .post-image,
.generate-columns-activated.post-image-aligned-right .generate-columns-container article:not(.featured-column) .post-image {
float: none;
text-align: center;
margin-left: 0;
margin-right: 0;
}
.generate-columns-container .paging-navigation,
.generate-columns-container .page-header {
flex: 1 1 100%;
clear: both;
}
.generate-columns-container .paging-navigation {
margin-bottom: 0;
}
.load-more:not(.has-svg-icon) .button.loading:before {
content: "\e900";
display: inline-block;
font-family: "GP Premium";
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
animation: spin 2s infinite linear;
margin-right: 7px;
}
.load-more .button:not(.loading) .gp-icon {
display: none;
}
.load-more .gp-icon svg {
animation: spin 2s infinite linear;
margin-right: 7px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.generate-columns {
box-sizing: border-box;
}
.generate-columns.grid-20,
.grid-sizer.grid-20 {
width: 20%;
}
.generate-columns.grid-25,
.grid-sizer.grid-25 {
width: 25%;
}
.generate-columns.grid-33,
.grid-sizer.grid-33 {
width: 33.3333%;
}
.generate-columns.grid-50,
.grid-sizer.grid-50 {
width: 50%;
}
.generate-columns.grid-60,
.grid-sizer.grid-60 {
width: 60%;
}
.generate-columns.grid-66,
.grid-sizer.grid-66 {
width: 66.66667%;
}
.generate-columns.grid-100,
.grid-sizer.grid-100 {
width: 100%;
}
@media (min-width: 768px) and (max-width: 1024px) {
.generate-columns.tablet-grid-50,
.grid-sizer.tablet-grid-50 {
width: 50%;
}
}
@media (max-width: 767px) {
.generate-columns-activated .generate-columns-container {
margin-left: 0;
margin-right: 0;
}
.generate-columns-container > *,
.generate-columns-container .generate-columns {
padding-left: 0;
}
.generate-columns-container .page-header {
margin-left: 0;
}
.generate-columns.mobile-grid-100,
.grid-sizer.mobile-grid-100 {
width: 100%;
}
.generate-columns-container > .paging-navigation {
margin-left: 0;
}
}
@media (max-width: 768px) {
.load-more {
display: block;
text-align: center;
margin-bottom: 0;
}
}

View File

@ -1 +0,0 @@
.masonry-enabled .page-header{position:relative!important}.separate-containers .site-main>.generate-columns-container{margin-bottom:0}.load-more.are-images-unloaded,.masonry-container.are-images-unloaded,.masonry-enabled #nav-below{opacity:0}.generate-columns-container:not(.masonry-container){display:flex;flex-wrap:wrap;align-items:stretch}.generate-columns .inside-article{height:100%;box-sizing:border-box}.generate-columns-activated.post-image-aligned-left .generate-columns-container article:not(.featured-column) .post-image,.generate-columns-activated.post-image-aligned-right .generate-columns-container article:not(.featured-column) .post-image{float:none;text-align:center;margin-left:0;margin-right:0}.generate-columns-container .page-header,.generate-columns-container .paging-navigation{flex:1 1 100%;clear:both}.generate-columns-container .paging-navigation{margin-bottom:0}.load-more:not(.has-svg-icon) .button.loading:before{content:"\e900";display:inline-block;font-family:"GP Premium";font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;animation:spin 2s infinite linear;margin-right:7px}.load-more .button:not(.loading) .gp-icon{display:none}.load-more .gp-icon svg{animation:spin 2s infinite linear;margin-right:7px}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.generate-columns{box-sizing:border-box}.generate-columns.grid-20,.grid-sizer.grid-20{width:20%}.generate-columns.grid-25,.grid-sizer.grid-25{width:25%}.generate-columns.grid-33,.grid-sizer.grid-33{width:33.3333%}.generate-columns.grid-50,.grid-sizer.grid-50{width:50%}.generate-columns.grid-60,.grid-sizer.grid-60{width:60%}.generate-columns.grid-66,.grid-sizer.grid-66{width:66.66667%}.generate-columns.grid-100,.grid-sizer.grid-100{width:100%}@media (min-width:768px) and (max-width:1024px){.generate-columns.tablet-grid-50,.grid-sizer.tablet-grid-50{width:50%}}@media (max-width:767px){.generate-columns-activated .generate-columns-container{margin-left:0;margin-right:0}.generate-columns-container .generate-columns,.generate-columns-container>*{padding-left:0}.generate-columns-container .page-header{margin-left:0}.generate-columns.mobile-grid-100,.grid-sizer.mobile-grid-100{width:100%}.generate-columns-container>.paging-navigation{margin-left:0}}@media (max-width:768px){.load-more{display:block;text-align:center;margin-bottom:0}}

View File

@ -1,104 +0,0 @@
.post-image-above-header .inside-article .post-image,
.post-image-above-header .inside-article .featured-image {
margin-top: 0;
margin-bottom: 2em;
}
.post-image-aligned-left .inside-article .post-image,
.post-image-aligned-left .inside-article .featured-image {
margin-top: 0;
margin-right: 2em;
float: left;
text-align: left;
}
.post-image-aligned-center .post-image,
.post-image-aligned-center .featured-image {
text-align: center;
}
.post-image-aligned-right .inside-article .post-image,
.post-image-aligned-right .inside-article .featured-image {
margin-top: 0;
margin-left: 2em;
float: right;
text-align: right;
}
.post-image-below-header.post-image-aligned-right .inside-article .post-image,
.post-image-below-header.post-image-aligned-right .inside-article .featured-image,
.post-image-below-header.post-image-aligned-center .inside-article .featured-image,
.post-image-below-header.post-image-aligned-left .inside-article .post-image,
.post-image-below-header.post-image-aligned-left .inside-article .featured-image {
margin-top: 2em;
}
.post-image-aligned-left > .featured-image,
.post-image-aligned-right > .featured-image {
float: none;
margin-left: auto;
margin-right: auto;
}
.post-image-aligned-left .featured-image {
text-align: left;
}
.post-image-aligned-right .featured-image {
text-align: right;
}
.post-image-aligned-left .inside-article:before,
.post-image-aligned-left .inside-article:after,
.post-image-aligned-right .inside-article:before,
.post-image-aligned-right .inside-article:after {
content: "";
display: table;
}
.post-image-aligned-left .inside-article:after,
.post-image-aligned-right .inside-article:after {
clear: both;
}
.one-container.post-image-above-header .page-header + .no-featured-image-padding .inside-article .post-image,
.one-container.post-image-above-header .no-featured-image-padding.generate-columns .inside-article .post-image {
margin-top: 0;
}
.one-container.right-sidebar.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.right-sidebar.post-image-aligned-center .no-featured-image-padding .featured-image,
.one-container.both-right.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.both-right.post-image-aligned-center .no-featured-image-padding .featured-image {
margin-right: 0;
}
.one-container.left-sidebar.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.left-sidebar.post-image-aligned-center .no-featured-image-padding .featured-image,
.one-container.both-left.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.both-left.post-image-aligned-center .no-featured-image-padding .featured-image {
margin-left: 0;
}
.one-container.both-sidebars.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.both-sidebars.post-image-aligned-center .no-featured-image-padding .featured-image {
margin-left: 0;
margin-right: 0;
}
.one-container.post-image-aligned-center .no-featured-image-padding.generate-columns .post-image,
.one-container.post-image-aligned-center .no-featured-image-padding.generate-columns .featured-image {
margin-left: 0;
margin-right: 0;
}
@media (max-width: 768px) {
body:not(.post-image-aligned-center) .inside-article .post-image,
body:not(.post-image-aligned-center) .featured-image,
body:not(.post-image-aligned-center) .inside-article .featured-image {
margin-right: 0;
margin-left: 0;
float: none;
text-align: center;
}
}

View File

@ -1 +0,0 @@
.post-image-above-header .inside-article .featured-image,.post-image-above-header .inside-article .post-image{margin-top:0;margin-bottom:2em}.post-image-aligned-left .inside-article .featured-image,.post-image-aligned-left .inside-article .post-image{margin-top:0;margin-right:2em;float:left;text-align:left}.post-image-aligned-center .featured-image,.post-image-aligned-center .post-image{text-align:center}.post-image-aligned-right .inside-article .featured-image,.post-image-aligned-right .inside-article .post-image{margin-top:0;margin-left:2em;float:right;text-align:right}.post-image-below-header.post-image-aligned-center .inside-article .featured-image,.post-image-below-header.post-image-aligned-left .inside-article .featured-image,.post-image-below-header.post-image-aligned-left .inside-article .post-image,.post-image-below-header.post-image-aligned-right .inside-article .featured-image,.post-image-below-header.post-image-aligned-right .inside-article .post-image{margin-top:2em}.post-image-aligned-left>.featured-image,.post-image-aligned-right>.featured-image{float:none;margin-left:auto;margin-right:auto}.post-image-aligned-left .featured-image{text-align:left}.post-image-aligned-right .featured-image{text-align:right}.post-image-aligned-left .inside-article:after,.post-image-aligned-left .inside-article:before,.post-image-aligned-right .inside-article:after,.post-image-aligned-right .inside-article:before{content:"";display:table}.post-image-aligned-left .inside-article:after,.post-image-aligned-right .inside-article:after{clear:both}.one-container.post-image-above-header .no-featured-image-padding.generate-columns .inside-article .post-image,.one-container.post-image-above-header .page-header+.no-featured-image-padding .inside-article .post-image{margin-top:0}.one-container.both-right.post-image-aligned-center .no-featured-image-padding .featured-image,.one-container.both-right.post-image-aligned-center .no-featured-image-padding .post-image,.one-container.right-sidebar.post-image-aligned-center .no-featured-image-padding .featured-image,.one-container.right-sidebar.post-image-aligned-center .no-featured-image-padding .post-image{margin-right:0}.one-container.both-left.post-image-aligned-center .no-featured-image-padding .featured-image,.one-container.both-left.post-image-aligned-center .no-featured-image-padding .post-image,.one-container.left-sidebar.post-image-aligned-center .no-featured-image-padding .featured-image,.one-container.left-sidebar.post-image-aligned-center .no-featured-image-padding .post-image{margin-left:0}.one-container.both-sidebars.post-image-aligned-center .no-featured-image-padding .featured-image,.one-container.both-sidebars.post-image-aligned-center .no-featured-image-padding .post-image{margin-left:0;margin-right:0}.one-container.post-image-aligned-center .no-featured-image-padding.generate-columns .featured-image,.one-container.post-image-aligned-center .no-featured-image-padding.generate-columns .post-image{margin-left:0;margin-right:0}@media (max-width:768px){body:not(.post-image-aligned-center) .featured-image,body:not(.post-image-aligned-center) .inside-article .featured-image,body:not(.post-image-aligned-center) .inside-article .post-image{margin-right:0;margin-left:0;float:none;text-align:center}}

View File

@ -1,254 +0,0 @@
.post-image-above-header .inside-article .post-image,
.post-image-above-header .inside-article .featured-image {
margin-top: 0;
margin-bottom: 2em;
}
.post-image-aligned-left .inside-article .post-image,
.post-image-aligned-left .inside-article .featured-image {
margin-top: 0;
margin-right: 2em;
float: left;
text-align: left;
}
.post-image-aligned-center .post-image,
.post-image-aligned-center .featured-image {
text-align: center;
}
.post-image-aligned-right .inside-article .post-image,
.post-image-aligned-right .inside-article .featured-image {
margin-top: 0;
margin-left: 2em;
float: right;
text-align: right;
}
.post-image-below-header.post-image-aligned-right .inside-article .post-image,
.post-image-below-header.post-image-aligned-right .inside-article .featured-image,
.post-image-below-header.post-image-aligned-center .inside-article .featured-image,
.post-image-below-header.post-image-aligned-left .inside-article .post-image,
.post-image-below-header.post-image-aligned-left .inside-article .featured-image {
margin-top: 2em;
}
.post-image-aligned-left > .featured-image,
.post-image-aligned-right > .featured-image {
float: none;
margin-left: auto;
margin-right: auto;
}
.post-image-aligned-left .featured-image {
text-align: left;
}
.post-image-aligned-right .featured-image {
text-align: right;
}
.post-image-aligned-left .inside-article:before,
.post-image-aligned-left .inside-article:after,
.post-image-aligned-right .inside-article:before,
.post-image-aligned-right .inside-article:after {
content: "";
display: table;
}
.post-image-aligned-left .inside-article:after,
.post-image-aligned-right .inside-article:after {
clear: both;
}
.one-container.post-image-above-header .page-header + .no-featured-image-padding .inside-article .post-image,
.one-container.post-image-above-header .no-featured-image-padding.generate-columns .inside-article .post-image {
margin-top: 0;
}
.one-container.right-sidebar.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.right-sidebar.post-image-aligned-center .no-featured-image-padding .featured-image,
.one-container.both-right.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.both-right.post-image-aligned-center .no-featured-image-padding .featured-image {
margin-right: 0;
}
.one-container.left-sidebar.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.left-sidebar.post-image-aligned-center .no-featured-image-padding .featured-image,
.one-container.both-left.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.both-left.post-image-aligned-center .no-featured-image-padding .featured-image {
margin-left: 0;
}
.one-container.both-sidebars.post-image-aligned-center .no-featured-image-padding .post-image,
.one-container.both-sidebars.post-image-aligned-center .no-featured-image-padding .featured-image {
margin-left: 0;
margin-right: 0;
}
.one-container.post-image-aligned-center .no-featured-image-padding.generate-columns .post-image,
.one-container.post-image-aligned-center .no-featured-image-padding.generate-columns .featured-image {
margin-left: 0;
margin-right: 0;
}
@media (max-width: 768px) {
body:not(.post-image-aligned-center) .inside-article .post-image,
body:not(.post-image-aligned-center) .featured-image,
body:not(.post-image-aligned-center) .inside-article .featured-image {
margin-right: 0;
margin-left: 0;
float: none;
text-align: center;
}
}
.masonry-enabled .page-header {
position: relative !important;
}
.separate-containers .site-main > .generate-columns-container {
margin-bottom: 0;
}
.masonry-container.are-images-unloaded,
.load-more.are-images-unloaded,
.masonry-enabled #nav-below {
opacity: 0;
}
/* columns */
.generate-columns-container:not(.masonry-container) {
display: flex;
flex-wrap: wrap;
align-items: stretch;
}
.generate-columns .inside-article {
height: 100%;
box-sizing: border-box;
}
.generate-columns-activated.post-image-aligned-left .generate-columns-container article:not(.featured-column) .post-image,
.generate-columns-activated.post-image-aligned-right .generate-columns-container article:not(.featured-column) .post-image {
float: none;
text-align: center;
margin-left: 0;
margin-right: 0;
}
.generate-columns-container .paging-navigation,
.generate-columns-container .page-header {
flex: 1 1 100%;
clear: both;
}
.generate-columns-container .paging-navigation {
margin-bottom: 0;
}
.load-more:not(.has-svg-icon) .button.loading:before {
content: "\e900";
display: inline-block;
font-family: "GP Premium";
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
animation: spin 2s infinite linear;
margin-right: 7px;
}
.load-more .button:not(.loading) .gp-icon {
display: none;
}
.load-more .gp-icon svg {
animation: spin 2s infinite linear;
margin-right: 7px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.generate-columns {
box-sizing: border-box;
}
.generate-columns.grid-20,
.grid-sizer.grid-20 {
width: 20%;
}
.generate-columns.grid-25,
.grid-sizer.grid-25 {
width: 25%;
}
.generate-columns.grid-33,
.grid-sizer.grid-33 {
width: 33.3333%;
}
.generate-columns.grid-50,
.grid-sizer.grid-50 {
width: 50%;
}
.generate-columns.grid-60,
.grid-sizer.grid-60 {
width: 60%;
}
.generate-columns.grid-66,
.grid-sizer.grid-66 {
width: 66.66667%;
}
.generate-columns.grid-100,
.grid-sizer.grid-100 {
width: 100%;
}
@media (min-width: 768px) and (max-width: 1024px) {
.generate-columns.tablet-grid-50,
.grid-sizer.tablet-grid-50 {
width: 50%;
}
}
@media (max-width: 767px) {
.generate-columns-activated .generate-columns-container {
margin-left: 0;
margin-right: 0;
}
.generate-columns-container > *,
.generate-columns-container .generate-columns {
padding-left: 0;
}
.generate-columns-container .page-header {
margin-left: 0;
}
.generate-columns.mobile-grid-100,
.grid-sizer.mobile-grid-100 {
width: 100%;
}
.generate-columns-container > .paging-navigation {
margin-left: 0;
}
}
@media (max-width: 768px) {
.load-more {
display: block;
text-align: center;
margin-bottom: 0;
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,58 +0,0 @@
<?php
defined( 'WPINC' ) or die;
if ( ! function_exists( 'generate_blog_get_defaults' ) ) {
/**
* Set our defaults
*
* @since 0.1
*/
function generate_blog_get_defaults() {
$generate_blog_defaults = array(
'excerpt_length' => '55',
'read_more' => __( 'Read more', 'gp-premium' ),
'read_more_button' => false,
'masonry' => false,
'masonry_load_more' => __( '+ More', 'gp-premium' ),
'masonry_loading' => __( 'Loading...', 'gp-premium' ),
'infinite_scroll' => false,
'infinite_scroll_button' => false,
'post_image' => true,
'post_image_position' => '',
'post_image_alignment' => 'post-image-aligned-center',
'post_image_size' => 'full',
'post_image_width' => '',
'post_image_height' => '',
'post_image_padding' => true,
'single_post_image' => true,
'single_post_image_position' => 'inside-content',
'single_post_image_alignment' => 'center',
'single_post_image_size' => 'full',
'single_post_image_width' => '',
'single_post_image_height' => '',
'single_post_image_padding' => true,
'page_post_image' => true,
'page_post_image_position' => 'above-content',
'page_post_image_alignment' => 'center',
'page_post_image_size' => 'full',
'page_post_image_width' => '',
'page_post_image_height' => '',
'page_post_image_padding' => true,
'date' => true,
'author' => true,
'categories' => true,
'tags' => true,
'comments' => true,
'single_date' => true,
'single_author' => true,
'single_categories' => true,
'single_tags' => true,
'single_post_navigation' => true,
'column_layout' => false,
'columns' => '50',
'featured_column' => false,
);
return apply_filters( 'generate_blog_option_defaults', $generate_blog_defaults );
}
}

View File

@ -1,714 +0,0 @@
<?php
defined( 'WPINC' ) or die;
// Pull in our defaults and functions
require plugin_dir_path( __FILE__ ) . 'defaults.php';
require plugin_dir_path( __FILE__ ) . 'images.php';
require plugin_dir_path( __FILE__ ) . 'columns.php';
require plugin_dir_path( __FILE__ ) . 'customizer.php';
require plugin_dir_path( __FILE__ ) . 'migrate.php';
if ( ! function_exists( 'generate_blog_scripts' ) ) {
add_action( 'wp_enqueue_scripts', 'generate_blog_scripts', 50 );
/**
* Enqueue scripts and styles
*/
function generate_blog_scripts() {
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
wp_add_inline_style( 'generate-style', generate_blog_css() );
wp_add_inline_style( 'generate-style', generate_blog_columns_css() );
$deps = array();
if ( 'true' == generate_blog_get_masonry() && generate_blog_get_columns() ) {
$deps[] = 'masonry';
$deps[] = 'imagesloaded';
}
if ( $settings[ 'infinite_scroll' ] && ! is_singular() && ! is_404() && ! is_post_type_archive( 'product' ) ) {
$deps[] = 'infinite-scroll';
wp_enqueue_script( 'infinite-scroll', plugin_dir_url( __FILE__ ) . 'js/infinite-scroll.pkgd.min.js', array(), '3.0.6', true );
$font_icons = true;
if ( function_exists( 'generate_get_option' ) ) {
if ( 'font' !== generate_get_option( 'icons' ) ) {
$font_icons = false;
}
}
if ( $settings['infinite_scroll_button'] && $font_icons ) {
wp_enqueue_style( 'gp-premium-icons' );
}
}
if ( ( 'true' == generate_blog_get_masonry() && generate_blog_get_columns() ) || ( $settings[ 'infinite_scroll' ] && ! is_singular() && ! is_404() && ! is_post_type_archive( 'product' ) ) ) {
wp_enqueue_script( 'generate-blog', plugin_dir_url( __FILE__ ) . "js/scripts{$suffix}.js", $deps, GENERATE_BLOG_VERSION, true );
wp_localize_script(
'generate-blog',
'generateBlog',
array(
'more' => $settings['masonry_load_more'],
'loading' => $settings['masonry_loading'],
'icon' => function_exists( 'generate_get_svg_icon' ) ? generate_get_svg_icon( 'spinner' ) : '',
'masonryInit' => apply_filters(
'generate_blog_masonry_init',
array(
'columnWidth' => '.grid-sizer',
'itemSelector' => '.masonry-post',
'stamp' => '.page-header',
'percentPosition' => true,
'stagger' => 30,
'visibleStyle' => array(
'transform' => 'translateY(0)',
'opacity' => 1,
),
'hiddenStyle' => array(
'transform' => 'translateY(5px)',
'opacity' => 0,
),
)
),
'infiniteScrollInit' => apply_filters(
'generate_blog_infinite_scroll_init',
array(
'path' => '.infinite-scroll-path a',
'append' => '#main .infinite-scroll-item',
'history' => false,
'loadOnScroll' => $settings['infinite_scroll_button'] ? false : true,
'button' => $settings['infinite_scroll_button'] ? '.load-more a' : null,
'scrollThreshold' => $settings['infinite_scroll_button'] ? false : 600,
)
),
)
);
}
$needs_columns_css = false;
$needs_featured_image_css = false;
if ( generate_blog_get_columns() || $settings['infinite_scroll'] ) {
$needs_columns_css = true;
}
if ( ! is_singular() ) {
if ( $settings['post_image'] ) {
$needs_featured_image_css = true;
}
}
if ( is_page() && has_post_thumbnail() ) {
if ( $settings['page_post_image'] ) {
$needs_featured_image_css = true;
}
}
if ( is_single() && has_post_thumbnail() ) {
if ( $settings['single_post_image'] ) {
$needs_featured_image_css = true;
}
}
if ( $needs_columns_css && $needs_featured_image_css ) {
wp_enqueue_style( 'generate-blog', plugin_dir_url( __FILE__ ) . "css/style{$suffix}.css", array(), GENERATE_BLOG_VERSION );
} elseif ( $needs_columns_css ) {
wp_enqueue_style( 'generate-blog-columns', plugin_dir_url( __FILE__ ) . "css/columns{$suffix}.css", array(), GENERATE_BLOG_VERSION );
} elseif ( $needs_featured_image_css ) {
wp_enqueue_style( 'generate-blog-images', plugin_dir_url( __FILE__ ) . "css/featured-images{$suffix}.css", array(), GENERATE_BLOG_VERSION );
}
}
}
if ( ! function_exists( 'generate_blog_post_classes' ) ) {
add_filter( 'post_class', 'generate_blog_post_classes' );
/**
* Adds custom classes to the content container
*
* @since 0.1
*/
function generate_blog_post_classes( $classes ) {
global $wp_query;
$paged = get_query_var( 'paged' );
$paged = $paged ? $paged : 1;
// Get our options
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( $settings['infinite_scroll'] ) {
$classes[] = 'infinite-scroll-item';
}
// Set our masonry class
if ( 'true' == generate_blog_get_masonry() && generate_blog_get_columns() ) {
$classes[] = 'masonry-post';
}
// Set our column classes
if ( generate_blog_get_columns() && ! is_singular() ) {
$classes[] = 'generate-columns';
$classes[] = 'tablet-grid-50';
$classes[] = 'mobile-grid-100';
$classes[] = 'grid-parent';
// Set our featured column class
if ( $wp_query->current_post == 0 && $paged == 1 && $settings['featured_column'] ) {
if ( 50 == generate_blog_get_column_count() ) {
$classes[] = 'grid-100';
}
if ( 33 == generate_blog_get_column_count() ) {
$classes[] = 'grid-66';
}
if ( 25 == generate_blog_get_column_count() ) {
$classes[] = 'grid-50';
}
if ( 20 == generate_blog_get_column_count() ) {
$classes[] = 'grid-60';
}
$classes[] = 'featured-column';
} else {
$classes[] = 'grid-' . generate_blog_get_column_count();
}
}
if ( ! $settings['post_image_padding'] && ! is_singular() ) {
$classes[] = 'no-featured-image-padding';
}
$location = generate_blog_get_singular_template();
if ( ! $settings[$location . '_post_image_padding'] && is_singular() ) {
$classes[] = 'no-featured-image-padding';
}
$atts = generate_get_blog_image_attributes();
if ( ! is_singular() && has_post_thumbnail() && ! empty( $atts ) ) {
$values = array(
$atts['width'],
$atts['height'],
$atts['crop'],
);
$image_src = wp_get_attachment_image_src( get_post_thumbnail_id( get_the_ID(), 'full' ), $values );
if ( $image_src && ( ! $image_src[3] || ! apply_filters( 'generate_use_featured_image_size_match', true ) ) ) {
$classes[] = 'resize-featured-image';
}
}
return $classes;
}
}
if ( ! function_exists( 'generate_blog_body_classes' ) ) {
add_filter( 'body_class', 'generate_blog_body_classes' );
/**
* Adds custom classes to the body
*
* @since 0.1
*/
function generate_blog_body_classes( $classes ) {
// Get theme options
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( is_singular() ) {
$location = generate_blog_get_singular_template();
if ( 'below-title' == $settings[$location . '_post_image_position'] ) {
$classes[] = 'post-image-below-header';
}
if ( 'inside-content' == $settings[$location . '_post_image_position'] ) {
$classes[] = 'post-image-above-header';
}
$classes[] = ( ! empty( $settings[$location . '_post_image_alignment'] ) ) ? 'post-image-aligned-' . $settings[$location . '_post_image_alignment'] : 'post-image-aligned-center';
} else {
$classes[] = ( '' == $settings['post_image_position'] ) ? 'post-image-below-header' : 'post-image-above-header';
$classes[] = ( ! empty( $settings['post_image_alignment'] ) ) ? $settings['post_image_alignment'] : 'post-image-aligned-center';
}
if ( 'true' == generate_blog_get_masonry() && generate_blog_get_columns() ) {
$classes[] = 'masonry-enabled';
}
if ( generate_blog_get_columns() && ! is_singular() ) {
$classes[] = 'generate-columns-activated';
}
if ( $settings[ 'infinite_scroll' ] && ! is_singular() ) {
$classes[] = 'infinite-scroll';
}
return $classes;
}
}
if ( ! function_exists( 'generate_excerpt_length' ) ) {
add_filter( 'excerpt_length', 'generate_excerpt_length', 15 );
/**
* Set our excerpt length
*
* @since 0.1
*/
function generate_excerpt_length( $length ) {
$generate_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
return absint( apply_filters( 'generate_excerpt_length', $generate_settings['excerpt_length'] ) );
}
}
if ( ! function_exists( 'generate_blog_css' ) ) {
/**
* Build our inline CSS
*
* @since 0.1
*/
function generate_blog_css() {
global $post;
$return = '';
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( ! defined( 'GENERATE_VERSION' ) ) {
return;
}
if ( version_compare( GENERATE_VERSION, '3.0.0-alpha.1', '<' ) ) {
// Get disable headline meta.
$disable_headline = ( isset( $post ) ) ? get_post_meta( $post->ID, '_generate-disable-headline', true ) : '';
if ( ! $settings['categories'] && ! $settings['comments'] && ! $settings['tags'] && ! is_singular() ) {
$return .= '.blog footer.entry-meta, .archive footer.entry-meta {display:none;}';
}
if ( ! $settings['single_date'] && ! $settings['single_author'] && $disable_headline && is_singular() ) {
$return .= '.single .entry-header{display:none;}.single .entry-content {margin-top:0;}';
}
if ( ! $settings['date'] && ! $settings['author'] && ! is_singular() ) {
$return .= '.entry-header .entry-meta {display:none;}';
}
if ( ! $settings['single_date'] && ! $settings['single_author'] && is_singular() ) {
$return .= '.entry-header .entry-meta {display:none;}';
}
if ( ! $settings['single_post_navigation'] && is_singular() ) {
$return .= '.post-navigation {display:none;}';
}
if ( ! $settings['single_categories'] && ! $settings['single_post_navigation'] && ! $settings['single_tags'] && is_singular() ) {
$return .= '.single footer.entry-meta {display:none;}';
}
}
$separator = 20;
$content_padding_top = 40;
$content_padding_right = 40;
$content_padding_left = 40;
$mobile_content_padding_top = 30;
$mobile_content_padding_right = 30;
$mobile_content_padding_left = 30;
if ( function_exists( 'generate_spacing_get_defaults' ) ) {
$spacing_settings = wp_parse_args(
get_option( 'generate_spacing_settings', array() ),
generate_spacing_get_defaults()
);
$separator = absint( $spacing_settings['separator'] );
$content_padding_top = absint( $spacing_settings['content_top'] );
$content_padding_right = absint( $spacing_settings['content_right'] );
$content_padding_left = absint( $spacing_settings['content_left'] );
$mobile_content_padding_top = absint( $spacing_settings['mobile_content_top'] );
$mobile_content_padding_right = absint( $spacing_settings['mobile_content_right'] );
$mobile_content_padding_left = absint( $spacing_settings['mobile_content_left'] );
}
if ( 'true' == generate_blog_get_masonry() && generate_blog_get_columns() ) {
$return .= '.page-header {margin-bottom: ' . $separator . 'px;margin-left: ' . $separator . 'px}';
}
if ( $settings[ 'infinite_scroll' ] && ! is_singular() ) {
$return .= '#nav-below {display:none;}';
}
if ( ! $settings['post_image_padding'] && 'post-image-aligned-center' == $settings['post_image_alignment'] && ! is_singular() ) {
$return .= '.no-featured-image-padding .post-image {margin-left:-' . $content_padding_left . 'px;margin-right:-' . $content_padding_right . 'px;}';
$return .= '.post-image-above-header .no-featured-image-padding .inside-article .post-image {margin-top:-' . $content_padding_top . 'px;}';
}
$location = generate_blog_get_singular_template();
if ( ! $settings[$location . '_post_image_padding'] && 'center' == $settings[$location . '_post_image_alignment'] && is_singular() ) {
$return .= '.no-featured-image-padding .featured-image {margin-left:-' . $content_padding_left . 'px;margin-right:-' . $content_padding_right . 'px;}';
$return .= '.post-image-above-header .no-featured-image-padding .inside-article .featured-image {margin-top:-' . $content_padding_top . 'px;}';
}
if ( ! $settings['page_post_image_padding'] || ! $settings['single_post_image_padding'] || ! $settings['post_image_padding'] ) {
$return .= '@media ' . generate_premium_get_media_query( 'mobile' ) . '{';
if ( ! $settings['post_image_padding'] && 'post-image-aligned-center' == $settings['post_image_alignment'] && ! is_singular() ) {
$return .= '.no-featured-image-padding .post-image {margin-left:-' . $mobile_content_padding_left . 'px;margin-right:-' . $mobile_content_padding_right . 'px;}';
$return .= '.post-image-above-header .no-featured-image-padding .inside-article .post-image {margin-top:-' . $mobile_content_padding_top . 'px;}';
}
if ( ! $settings[$location . '_post_image_padding'] && 'center' == $settings[$location . '_post_image_alignment'] && is_singular() ) {
$return .= '.no-featured-image-padding .featured-image {margin-left:-' . $mobile_content_padding_left . 'px;margin-right:-' . $mobile_content_padding_right . 'px;}';
$return .= '.post-image-above-header .no-featured-image-padding .inside-article .featured-image {margin-top:-' . $mobile_content_padding_top . 'px;}';
}
$return .= '}';
}
$atts = generate_get_blog_image_attributes();
if ( ! empty( $atts ) ) {
$image_width = $atts['width'] && 9999 !== $atts['width'] ? 'width: ' . $atts['width'] . 'px;' : '';
$image_height = $atts['height'] && 9999 !== $atts['height'] ? 'height: ' . $atts['height'] . 'px;' : '';
$image_crop = $atts['crop'] ? '-o-object-fit: cover;object-fit: cover;' : '';
if ( ! $image_width && $image_height ) {
$image_crop = '-o-object-fit: cover;object-fit: cover;';
}
if ( ! is_singular() ) {
$return .= '.resize-featured-image .post-image img {' . $image_width . $image_height . $image_crop . '}';
}
if ( is_single() || is_page() ) {
$values = array(
$atts['width'],
$atts['height'],
$atts['crop'],
);
$image_src = wp_get_attachment_image_src( get_post_thumbnail_id( get_the_ID(), 'full' ), $values );
if ( $image_src && ( ! $image_src[3] || ! apply_filters( 'generate_use_featured_image_size_match', true ) ) ) {
$return .= '.featured-image img {' . $image_width . $image_height . $image_crop . '}';
}
}
}
return $return;
}
}
if ( ! function_exists( 'generate_blog_excerpt_more' ) ) {
add_filter( 'excerpt_more', 'generate_blog_excerpt_more', 15 );
/**
* Prints the read more HTML
*/
function generate_blog_excerpt_more( $more ) {
$generate_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( '' == $generate_settings['read_more'] ) {
return '';
}
return apply_filters(
'generate_excerpt_more_output',
sprintf(
' ... <a title="%1$s" class="read-more" href="%2$s" aria-label="%4$s">%3$s</a>',
the_title_attribute( 'echo=0' ),
esc_url( get_permalink( get_the_ID() ) ),
wp_kses_post( $generate_settings['read_more'] ),
sprintf(
/* translators: Aria-label describing the read more button */
_x( 'More on %s', 'more on post title', 'gp-premium' ),
the_title_attribute( 'echo=0' )
)
)
);
}
}
if ( ! function_exists( 'generate_blog_content_more' ) ) {
add_filter( 'the_content_more_link', 'generate_blog_content_more', 15 );
/**
* Prints the read more HTML
*/
function generate_blog_content_more( $more ) {
$generate_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( '' == $generate_settings['read_more'] ) {
return '';
}
return apply_filters(
'generate_content_more_link_output',
sprintf(
'<p class="read-more-container"><a title="%1$s" class="read-more content-read-more" href="%2$s" aria-label="%4$s">%3$s</a></p>',
the_title_attribute( 'echo=0' ),
esc_url( get_permalink( get_the_ID() ) . apply_filters( 'generate_more_jump', '#more-' . get_the_ID() ) ),
wp_kses_post( $generate_settings['read_more'] ),
sprintf(
/* translators: Aria-label describing the read more button */
_x( 'More on %s', 'more on post title', 'gp-premium' ),
the_title_attribute( 'echo=0' )
)
)
);
}
}
/**
* Checks the setting and returns false if $thing is disabled
*
* @since 1.4
*
* @param String $data The original data, passed through if not disabled
* @param String $thing The name of the thing to check
* @return String|False The original data, or false (if disabled)
*/
function generate_disable_post_thing( $data, $thing ) {
$generate_blog_settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( ! $generate_blog_settings[$thing] ) {
return false;
}
return $data;
}
if ( ! function_exists( 'generate_disable_post_date' ) ) {
add_filter( 'generate_post_date', 'generate_disable_post_date' );
/**
* Remove the post date if set
*
* @since 0.1
*/
function generate_disable_post_date( $date ) {
if ( is_singular() ) {
return generate_disable_post_thing( $date, 'single_date' );
} else {
return generate_disable_post_thing( $date, 'date' );
}
}
}
if ( ! function_exists( 'generate_disable_post_author' ) ) {
add_filter( 'generate_post_author', 'generate_disable_post_author' );
/**
* Set the author if set
*
* @since 0.1
*/
function generate_disable_post_author( $author ) {
if ( is_singular() ) {
return generate_disable_post_thing( $author, 'single_author' );
} else {
return generate_disable_post_thing( $author, 'author' );
}
}
}
if ( ! function_exists( 'generate_disable_post_categories' ) ) {
add_filter( 'generate_show_categories', 'generate_disable_post_categories' );
/**
* Remove the categories if set
*
* @since 0.1
*/
function generate_disable_post_categories( $categories ) {
if ( is_singular() ) {
return generate_disable_post_thing( $categories, 'single_categories' );
} else {
return generate_disable_post_thing( $categories, 'categories' );
}
}
}
if ( ! function_exists( 'generate_disable_post_tags' ) ) {
add_filter( 'generate_show_tags', 'generate_disable_post_tags' );
/**
* Remove the tags if set
*
* @since 0.1
*/
function generate_disable_post_tags( $tags ) {
if ( is_singular() ) {
return generate_disable_post_thing( $tags, 'single_tags' );
} else {
return generate_disable_post_thing( $tags, 'tags' );
}
}
}
if ( ! function_exists( 'generate_disable_post_comments_link' ) ) {
add_filter( 'generate_show_comments', 'generate_disable_post_comments_link' );
/**
* Remove the link to comments if set
*
* @since 0.1
*/
function generate_disable_post_comments_link( $comments_link ) {
return generate_disable_post_thing( $comments_link, 'comments' );
}
}
add_filter( 'generate_show_post_navigation', 'generate_disable_post_navigation' );
/**
* Remove the single post navigation
*
* @since 1.5
*/
function generate_disable_post_navigation( $navigation ) {
if ( is_singular() ) {
return generate_disable_post_thing( $navigation, 'single_post_navigation' );
} else {
return $navigation;
}
}
add_filter( 'generate_excerpt_more_output', 'generate_blog_read_more_button' );
add_filter( 'generate_content_more_link_output', 'generate_blog_read_more_button' );
/**
* Add the button class to our read more link if set.
*
* @since 1.5
*
* @param string $output Our existing read more link.
*/
function generate_blog_read_more_button( $output ) {
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( ! $settings['read_more_button'] ) {
return $output;
}
return sprintf(
'%5$s<p class="read-more-container"><a title="%1$s" class="read-more button" href="%2$s" aria-label="%4$s">%3$s</a></p>',
the_title_attribute( 'echo=0' ),
esc_url( get_permalink( get_the_ID() ) . apply_filters( 'generate_more_jump', '#more-' . get_the_ID() ) ),
wp_kses_post( $settings['read_more'] ),
sprintf(
/* translators: Aria-label describing the read more button */
_x( 'More on %s', 'more on post title', 'gp-premium' ),
the_title_attribute( 'echo=0' )
),
'generate_excerpt_more_output' === current_filter() ? ' ... ' : ''
);
}
if ( ! function_exists( 'generate_blog_load_more' ) ) {
add_action( 'generate_after_main_content', 'generate_blog_load_more', 20 );
/**
* Build our load more button
*/
function generate_blog_load_more() {
// Get theme options
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( ( ! $settings[ 'infinite_scroll_button' ] || ! $settings[ 'infinite_scroll' ] ) || is_singular() || is_404() ) {
return;
}
global $wp_query;
if ( $wp_query->max_num_pages < 2 ) {
return;
}
if ( is_post_type_archive( 'product' ) ) {
return;
}
if ( is_tax( 'product_cat' ) ) {
return;
}
$icon = '';
if ( function_exists( 'generate_get_svg_icon' ) ) {
$icon = generate_get_svg_icon( 'spinner' );
}
printf(
'<div class="masonry-load-more load-more %1$s %2$s">
<a class="button" href="#">%3$s%4$s</a>
</div>',
$icon ? 'has-svg-icon' : '',
( 'true' == generate_blog_get_masonry() && generate_blog_get_columns() ) ? 'are-images-unloaded' : '',
$icon,
wp_kses_post( $settings['masonry_load_more'] )
);
}
}
/**
* Checks to see whether we're getting page or single post options.
*
* @since 1.5
*
* @return string Name of our singular template
*/
function generate_blog_get_singular_template() {
$template = 'single';
if ( is_page() ) {
$template = 'page';
}
return $template;
}
add_action( 'generate_after_footer', 'generate_blog_do_infinite_scroll_path', 500 );
/**
* Add a next page of posts link for infinite scroll.
*
* @since 2.0.0
*/
function generate_blog_do_infinite_scroll_path() {
if ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) {
return;
}
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( $settings['infinite_scroll'] && ! is_singular() && ! is_404() ) {
printf(
'<div class="infinite-scroll-path" aria-hidden="true" style="display: none;">%s</div>',
get_next_posts_link()
);
}
}

View File

@ -1,369 +0,0 @@
<?php
/**
* Functions specific to featured images.
*
* @package GP Premium
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // No direct access, please.
}
/**
* Collects all available image sizes which we use in the Customizer.
*
* @since 1.10.0
*
* @return array
*/
function generate_blog_get_image_sizes() {
$sizes = get_intermediate_image_sizes();
$new_sizes = array(
'full' => 'full',
);
foreach ( $sizes as $key => $name ) {
$new_sizes[ $name ] = $name;
}
return $new_sizes;
}
add_filter( 'generate_page_header_default_size', 'generate_blog_set_featured_image_size' );
/**
* Set our featured image sizes.
*
* @since 1.10.0
*
* @param string $size The existing size.
* @return string The new size.
*/
function generate_blog_set_featured_image_size( $size ) {
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( ! is_singular() ) {
$size = $settings['post_image_size'];
}
if ( is_single() ) {
$size = $settings['single_post_image_size'];
}
if ( is_page() ) {
$size = $settings['page_post_image_size'];
}
$atts = generate_get_blog_image_attributes();
if ( ! empty( $atts ) ) {
$values = array(
$atts['width'],
$atts['height'],
$atts['crop'],
);
$image_src = wp_get_attachment_image_src( get_post_thumbnail_id( get_the_ID(), 'full' ), $values );
if ( $image_src && $image_src[3] && apply_filters( 'generate_use_featured_image_size_match', true ) ) {
return $values;
} else {
return $size;
}
}
return $size;
}
if ( ! function_exists( 'generate_get_blog_image_attributes' ) ) {
/**
* Build our image attributes
*
* @since 0.1
*/
function generate_get_blog_image_attributes() {
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( is_singular() ) {
if ( is_singular( 'page' ) ) {
$single = 'page_';
} else {
$single = 'single_';
}
} else {
$single = '';
}
$ignore_crop = array( '', '0', '9999' );
$atts = array(
'width' => ( in_array( $settings[ "{$single}post_image_width" ], $ignore_crop ) ) ? 9999 : absint( $settings[ "{$single}post_image_width" ] ),
'height' => ( in_array( $settings[ "{$single}post_image_height" ], $ignore_crop ) ) ? 9999 : absint( $settings[ "{$single}post_image_height" ] ),
'crop' => ( in_array( $settings[ "{$single}post_image_width" ], $ignore_crop ) || in_array( $settings[ "{$single}post_image_height" ], $ignore_crop ) ) ? false : true,
);
// If there's no height or width, empty the array.
if ( 9999 == $atts['width'] && 9999 == $atts['height'] ) { // phpcs:ignore
$atts = array();
}
return apply_filters( 'generate_blog_image_attributes', $atts );
}
}
if ( ! function_exists( 'generate_blog_setup' ) ) {
add_action( 'wp', 'generate_blog_setup', 50 );
/**
* Setup our blog functions and actions
*
* @since 0.1
*/
function generate_blog_setup() {
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
// Move our featured images to above the title.
if ( 'post-image-above-header' === $settings['post_image_position'] ) {
remove_action( 'generate_after_entry_header', 'generate_post_image' );
add_action( 'generate_before_content', 'generate_post_image' );
// If we're using the Page Header add-on, move those as well.
if ( function_exists( 'generate_page_header_post_image' ) ) {
remove_action( 'generate_after_entry_header', 'generate_page_header_post_image' );
add_action( 'generate_before_content', 'generate_page_header_post_image' );
}
}
$page_header_content = false;
if ( function_exists( 'generate_page_header_get_options' ) ) {
$options = generate_page_header_get_options();
if ( $options && '' !== $options['content'] ) {
$page_header_content = true;
}
// If our Page Header has no content, remove it.
// This will allow the Blog add-on to add an image for us.
if ( ! $page_header_content && is_singular() ) {
remove_action( 'generate_before_content', 'generate_page_header' );
remove_action( 'generate_after_entry_header', 'generate_page_header' );
remove_action( 'generate_after_header', 'generate_page_header' );
}
}
// Remove the core theme featured image.
// I would like to filter instead one day.
remove_action( 'generate_after_header', 'generate_featured_page_header' );
remove_action( 'generate_before_content', 'generate_featured_page_header_inside_single' );
$location = generate_blog_get_singular_template();
if ( $settings[ $location . '_post_image' ] && is_singular() && ! $page_header_content ) {
if ( 'below-title' === $settings[ $location . '_post_image_position' ] ) {
add_action( 'generate_after_entry_header', 'generate_blog_single_featured_image' );
}
if ( 'inside-content' === $settings[ $location . '_post_image_position' ] ) {
add_action( 'generate_before_content', 'generate_blog_single_featured_image' );
}
if ( 'above-content' === $settings[ $location . '_post_image_position' ] ) {
add_action( 'generate_after_header', 'generate_blog_single_featured_image' );
}
}
}
}
add_filter( 'generate_featured_image_output', 'generate_blog_featured_image' );
/**
* Remove featured image if set or using WooCommerce.
*
* @since 1.5
* @param string $output The existing output.
* @return string The image HTML
*/
function generate_blog_featured_image( $output ) {
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) || ! $settings['post_image'] ) {
return false;
}
return $output;
}
/**
* Build our featured images for single posts and pages.
*
* This function is way more complicated than it could be so it can
* ensure compatibility with the Page Header add-on.
*
* @since 1.5
*
* @return string The image HTML
*/
function generate_blog_single_featured_image() {
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
$image_id = get_post_thumbnail_id( get_the_ID(), 'full' );
if ( function_exists( 'generate_page_header_get_image' ) && generate_page_header_get_image( 'ID' ) ) {
if ( intval( $image_id ) !== generate_page_header_get_image( 'ID' ) ) {
$image_id = generate_page_header_get_image( 'ID' );
}
}
$location = generate_blog_get_singular_template();
if ( ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) || ! $settings[ $location . '_post_image' ] || ! $image_id ) {
return false;
}
$attrs = array(
'itemprop' => 'image',
);
if ( function_exists( 'generate_get_schema_type' ) ) {
if ( 'microdata' !== generate_get_schema_type() ) {
$attrs = array();
}
}
$attrs['loading'] = false;
$attrs = apply_filters( 'generate_single_featured_image_attrs', $attrs );
$image_html = apply_filters(
'post_thumbnail_html', // phpcs:ignore -- Core filter.
wp_get_attachment_image(
$image_id,
apply_filters( 'generate_page_header_default_size', 'full' ),
'',
$attrs
),
get_the_ID(),
$image_id,
apply_filters( 'generate_page_header_default_size', 'full' ),
''
);
$location = generate_blog_get_singular_template();
$classes = array(
is_page() ? 'page-header-image' : null,
is_singular() && ! is_page() ? 'page-header-image-single' : null,
'above-content' === $settings[ $location . '_post_image_position' ] ? 'grid-container grid-parent' : null,
);
$image_html = apply_filters( 'generate_single_featured_image_html', $image_html );
// phpcs:ignore -- No need to escape here.
echo apply_filters(
'generate_single_featured_image_output',
sprintf(
'<div class="featured-image %2$s">
%1$s
</div>',
$image_html,
implode( ' ', $classes )
),
$image_html
);
}
add_filter( 'generate_blog_image_attributes', 'generate_blog_page_header_image_atts' );
/**
* Filter our image attributes in case we're using differents atts in our Page Header
*
* @since 1.5
*
* @param array $atts Our existing image attributes.
* @return array Image attributes
*/
function generate_blog_page_header_image_atts( $atts ) {
if ( ! function_exists( 'generate_page_header_get_options' ) ) {
return $atts;
}
if ( ! is_singular() ) {
return $atts;
}
$options = generate_page_header_get_options();
if ( $options && 'enable' === $options['image_resize'] ) {
$ignore_crop = array( '', '0', '9999' );
$atts = array(
'width' => ( in_array( $options['image_width'], $ignore_crop ) ) ? 9999 : absint( $options['image_width'] ),
'height' => ( in_array( $options['image_height'], $ignore_crop ) ) ? 9999 : absint( $options['image_height'] ),
'crop' => ( in_array( $options['image_width'], $ignore_crop ) || in_array( $options['image_height'], $ignore_crop ) ) ? false : true,
);
}
return $atts;
}
add_filter( 'generate_single_featured_image_html', 'generate_blog_page_header_link' );
/**
* Add our Page Header link to our featured image if set.
*
* @since 1.5
*
* @param string $image_html Our existing image HTML.
* @return string Our new image HTML.
*/
function generate_blog_page_header_link( $image_html ) {
if ( ! function_exists( 'generate_page_header_get_options' ) ) {
return $image_html;
}
$options = generate_page_header_get_options();
if ( ! empty( $options['image_link'] ) ) {
return '<a href="' . esc_url( $options['image_link'] ) . '"' . apply_filters( 'generate_page_header_link_target', '' ) . '>' . $image_html . '</a>';
} else {
return $image_html;
}
}
add_filter( 'body_class', 'generate_blog_remove_featured_image_class', 20 );
/**
* Remove the featured image classes if they're disabled.
*
* @since 2.1.0
* @param array $classes The body classes.
*/
function generate_blog_remove_featured_image_class( $classes ) {
if ( is_singular() ) {
$settings = wp_parse_args(
get_option( 'generate_blog_settings', array() ),
generate_blog_get_defaults()
);
if ( is_single() ) {
$disable_single_featured_image = ! $settings['single_post_image'];
$classes = generate_premium_remove_featured_image_class( $classes, $disable_single_featured_image );
}
if ( is_page() && ! $settings['page_post_image'] ) {
$disable_page_featured_image = ! $settings['page_post_image'];
$classes = generate_premium_remove_featured_image_class( $classes, $disable_page_featured_image );
}
}
return $classes;
}

View File

@ -1,98 +0,0 @@
jQuery( function( $ ) {
// Featured image controls
var featuredImageArchiveControls = [
'generate_blog_settings-post_image',
'generate_blog_settings-post_image_padding',
'generate_blog_settings-post_image_position',
'generate_blog_settings-post_image_alignment',
'generate_blog_settings-post_image_size',
'generate_blog_settings-post_image_width',
'generate_blog_settings-post_image_height',
'generate_regenerate_images_notice',
];
$.each( featuredImageArchiveControls, function( index, value ) {
$( '#customize-control-' + value ).attr( 'data-control-section', 'featured-image-archives' );
} );
var featuredImageSingleControls = [
'generate_blog_settings-single_post_image',
'generate_blog_settings-single_post_image_padding',
'generate_blog_settings-single_post_image_position',
'generate_blog_settings-single_post_image_alignment',
'generate_blog_settings-single_post_image_size',
'generate_blog_settings-single_post_image_width',
'generate_blog_settings-single_post_image_height',
'generate_regenerate_single_post_images_notice',
];
$.each( featuredImageSingleControls, function( index, value ) {
$( '#customize-control-' + value ).attr( 'data-control-section', 'featured-image-single' ).css( {
visibility: 'hidden',
height: '0',
width: '0',
margin: '0',
overflow: 'hidden',
} );
} );
var featuredImagePageControls = [
'generate_blog_settings-page_post_image',
'generate_blog_settings-page_post_image_padding',
'generate_blog_settings-page_post_image_position',
'generate_blog_settings-page_post_image_alignment',
'generate_blog_settings-page_post_image_size',
'generate_blog_settings-page_post_image_width',
'generate_blog_settings-page_post_image_height',
'generate_regenerate_page_images_notice',
];
$.each( featuredImagePageControls, function( index, value ) {
$( '#customize-control-' + value ).attr( 'data-control-section', 'featured-image-page' ).css( {
visibility: 'hidden',
height: '0',
width: '0',
margin: '0',
overflow: 'hidden',
} );
} );
// Post meta controls
var postMetaArchiveControls = [
'generate_settings-post_content',
'generate_blog_settings-excerpt_length',
'generate_blog_settings-read_more',
'generate_blog_settings-read_more_button',
'generate_blog_settings-date',
'generate_blog_settings-author',
'generate_blog_settings-categories',
'generate_blog_settings-tags',
'generate_blog_settings-comments',
'generate_blog_settings-infinite_scroll',
'generate_blog_settings-infinite_scroll_button',
'blog_masonry_load_more_control',
'blog_masonry_loading_control',
];
$.each( postMetaArchiveControls, function( index, value ) {
$( '#customize-control-' + value ).attr( 'data-control-section', 'post-meta-archives' );
} );
var postMetaSingleControls = [
'generate_blog_settings-single_date',
'generate_blog_settings-single_author',
'generate_blog_settings-single_categories',
'generate_blog_settings-single_tags',
'generate_blog_settings-single_post_navigation',
];
$.each( postMetaSingleControls, function( index, value ) {
$( '#customize-control-' + value ).attr( 'data-control-section', 'post-meta-single' ).css( {
visibility: 'hidden',
height: '0',
width: '0',
margin: '0',
overflow: 'hidden',
} );
} );
} );

View File

@ -1,76 +0,0 @@
/**
* Theme Customizer enhancements for a better user experience.
*
* Contains handlers to make Theme Customizer preview reload changes asynchronously.
*/
( function( $ ) {
// Container width
wp.customize( 'generate_settings[container_width]', function( value ) {
value.bind( function() {
if ( $( '.masonry-container' )[ 0 ] ) {
jQuery( '.masonry-container' ).imagesLoaded( function() {
$container = jQuery( '.masonry-container' );
if ( jQuery( $container ).length ) {
$container.masonry( {
columnWidth: '.grid-sizer',
itemSelector: '.masonry-post',
stamp: '.page-header',
} );
}
} );
}
} );
} );
$( 'body' ).on( 'generate_spacing_updated', function() {
if ( $( '.masonry-container' )[ 0 ] ) {
jQuery( '.masonry-container' ).imagesLoaded( function() {
$container = jQuery( '.masonry-container' );
if ( jQuery( $container ).length ) {
$container.masonry( {
columnWidth: '.grid-sizer',
itemSelector: '.masonry-post',
stamp: '.page-header',
} );
}
} );
}
} );
/**
* The first infinite scroll load in the Customizer misses article classes if they've been
* added or removed in the previous refresh.
*
* This is totally hacky, but I'm just happy I finally got it working!
*/
var $container = $( '.infinite-scroll-item' ).first().parent();
$container.on( 'load.infiniteScroll', function( event, response ) {
var $posts = $( response ).find( 'article' );
if ( wp.customize.value( 'generate_blog_settings[column_layout]' )() ) {
$posts.addClass( 'generate-columns' );
$posts.addClass( 'grid-parent' );
$posts.addClass( 'grid-' + wp.customize.value( 'generate_blog_settings[columns]' )() );
$posts.addClass( 'tablet-grid-50' );
$posts.addClass( 'mobile-grid-100' );
} else {
$posts.removeClass( 'generate-columns' );
$posts.removeClass( 'grid-parent' );
$posts.removeClass( 'grid-' + wp.customize.value( 'generate_blog_settings[columns]' )() );
$posts.removeClass( 'tablet-grid-50' );
$posts.removeClass( 'mobile-grid-100' );
}
if ( wp.customize.value( 'generate_blog_settings[masonry]' )() ) {
$posts.addClass( 'masonry-post' );
} else {
$posts.removeClass( 'masonry-post' );
}
if ( ! wp.customize.value( 'generate_blog_settings[post_image_padding]' )() ) {
$posts.addClass( 'no-featured-image-padding' );
} else {
$posts.removeClass( 'no-featured-image-padding' );
}
} );
}( jQuery ) );

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More