updated plugin ActivityPub version 8.3.0
@ -0,0 +1,7 @@
|
||||
/* Admin Bar Social Web Icon */
|
||||
#wpadminbar .activitypub-admin-bar-social-web .ab-item::before {
|
||||
content: "\2042"; /* ⁂ Asterism */
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-weight: 700;
|
||||
top: 2px;
|
||||
}
|
||||
@ -4,12 +4,12 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.settings_page_activitypub .notice {
|
||||
.settings_page_activitypub div:not(.wrap) > .notice {
|
||||
max-width: 800px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
.settings_page_activitypub .update-nag {
|
||||
.settings_page_activitypub div:not(.wrap) > .update-nag {
|
||||
margin: 25px 20px 15px 22px;
|
||||
}
|
||||
|
||||
@ -17,6 +17,10 @@
|
||||
padding-left: 22px;
|
||||
}
|
||||
|
||||
.activitypub-settings p.interactions {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.activitypub-settings-header {
|
||||
text-align: center;
|
||||
margin: 0 0 1rem;
|
||||
@ -45,6 +49,14 @@
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.activitypub-settings-tabs-scroller {
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
padding-top: 2px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.activitypub-settings-tabs-wrapper {
|
||||
display: inline-flex;
|
||||
vertical-align: top;
|
||||
@ -61,9 +73,10 @@
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
padding: .5rem 1rem 1rem;
|
||||
padding: 0.5rem 1rem 1rem;
|
||||
margin: 0 1rem;
|
||||
transition: box-shadow .5s ease-in-out;
|
||||
transition: box-shadow 0.5s ease-in-out;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.activitypub-settings .row {
|
||||
@ -91,6 +104,25 @@ summary {
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.activitypub-site-block-details {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.activitypub-site-block-details summary {
|
||||
padding: 8px 0;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.activitypub-site-block-details table {
|
||||
max-width: 500px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.activitypub-site-block-details td:last-child {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.activitypub-settings-accordion {
|
||||
border: 1px solid #c3c4c7;
|
||||
}
|
||||
@ -133,13 +165,6 @@ summary {
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
.activitypub-settings-accordion-trigger {
|
||||
color: #2c3338;
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.activitypub-settings-accordion-trigger .title {
|
||||
pointer-events: none;
|
||||
font-weight: 600;
|
||||
@ -150,19 +175,24 @@ summary {
|
||||
.activitypub-settings-accordion-viewed .icon {
|
||||
border: solid #50575e medium;
|
||||
border-width: 0 2px 2px 0;
|
||||
height: .5rem;
|
||||
height: 0.5rem;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 1.5em;
|
||||
top: 50%;
|
||||
transform: translateY(-70%) rotate(45deg);
|
||||
width: .5rem;
|
||||
width: 0.5rem;
|
||||
}
|
||||
|
||||
.activitypub-settings-accordion-trigger[aria-expanded="true"] .icon {
|
||||
transform: translateY(-30%) rotate(-135deg);
|
||||
}
|
||||
|
||||
table.followings .dashicons {
|
||||
font-size: 1em;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.activitypub-settings-accordion-trigger:active,
|
||||
.activitypub-settings-accordion-trigger:hover {
|
||||
background: #f6f7f7;
|
||||
@ -189,8 +219,8 @@ input.blog-user-identifier {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-bottom: 40px;
|
||||
background-image: rgb(168,165,175);
|
||||
background-image: linear-gradient(180deg, red, yellow);
|
||||
background-image: rgb(168, 165, 175);
|
||||
background-image: linear-gradient(180deg, #f00, #ff0);
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
@ -207,66 +237,178 @@ input.blog-user-identifier {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-id-pattern -- WordPress core ID */
|
||||
#dashboard_right_now li a.activitypub-followers::before {
|
||||
content: "\f307";
|
||||
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
|
||||
font-family: dashicons;
|
||||
}
|
||||
|
||||
.repost .dashboard-comment-wrap,
|
||||
.like .dashboard-comment-wrap {
|
||||
.activitypub-comment .dashboard-comment-wrap {
|
||||
padding-inline-start: 63px;
|
||||
}
|
||||
|
||||
.repost .dashboard-comment-wrap .comment-author,
|
||||
.like .dashboard-comment-wrap .comment-author {
|
||||
.activitypub-comment .dashboard-comment-wrap .comment-author {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
.activitypub-settings .welcome-tab-close {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
font-size: 13px;
|
||||
padding: 0 5px 0 20px;
|
||||
text-decoration: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.activitypub-settings .welcome-tab-close::before {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0;
|
||||
transition: all .1s ease-in-out;
|
||||
font: normal 16px/20px dashicons;
|
||||
content: '\f335';
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.activitypub-notice .count {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
margin: 1px 0 -1px 2px;
|
||||
padding: 0 5px;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 9px;
|
||||
background-color: #dba617;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
z-index: 26;
|
||||
}
|
||||
|
||||
.activitypub-notice .dashicons-warning {
|
||||
color: #dba617;
|
||||
}
|
||||
|
||||
.extra-fields-nav a + a {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.rtl .extra-fields-nav a + a {
|
||||
margin-left: auto;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap dt {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap .activitypub-block-screenshot {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap .activitypub-block-screenshot img {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap .activitypub-block-screenshot figcaption {
|
||||
color: #555;
|
||||
font-style: italic;
|
||||
font-size: 0.9em;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Blockquote Styles */
|
||||
.contextual-help-tabs-wrap blockquote {
|
||||
border-left: 4px solid #3582c4;
|
||||
background-color: #f6f7f7;
|
||||
padding: 16px 20px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap blockquote p {
|
||||
margin: 0 0 10px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap blockquote p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap blockquote cite {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-top: 8px;
|
||||
font-size: 0.9em;
|
||||
color: #50575e;
|
||||
}
|
||||
|
||||
.contextual-help-tabs-wrap blockquote cite::before {
|
||||
content: "—";
|
||||
}
|
||||
|
||||
/* Plugin List Styles */
|
||||
.plugin-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.plugin-list .plugin-card {
|
||||
flex: 1 1 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.plugin-list .plugin-card .desc {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.plugin-list .plugin-action-buttons li {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.plugin-list .plugin-card .action-links {
|
||||
position: static;
|
||||
margin-left: 148px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.plugin-list .plugin-action-buttons {
|
||||
float: none;
|
||||
margin: 1em 0 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.plugin-list .plugin-action-buttons li .button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.plugin-list .plugin-card h3 {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
/* stylelint-disable no-descending-specificity */
|
||||
.plugin-list .plugin-card .name,
|
||||
.plugin-list .plugin-card .desc,
|
||||
.plugin-card .desc > p {
|
||||
margin-right: 0;
|
||||
}
|
||||
/* stylelint-enable no-descending-specificity */
|
||||
|
||||
.plugin-list .plugin-card .desc p:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* RTL Support for blockquotes */
|
||||
.rtl .contextual-help-tabs-wrap blockquote {
|
||||
border-left: none;
|
||||
border-right: 4px solid #3582c4;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
#activitypub-follow-form .highlight {
|
||||
animation: highlight-fade 3s ease-in-out;
|
||||
border-color: #3582c4 !important;
|
||||
box-shadow: 0 0 0 1px #3582c4;
|
||||
}
|
||||
|
||||
@keyframes highlight-fade {
|
||||
|
||||
0% {
|
||||
background-color: #e7f3ff;
|
||||
border-color: #3582c4;
|
||||
box-shadow: 0 0 0 1px #3582c4;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: #fff;
|
||||
border-color: #8c8f94;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
|
||||
.activitypub-settings {
|
||||
margin: 0 22px;
|
||||
}
|
||||
|
||||
.activitypub-settings .row > div {
|
||||
max-width: calc(100% - 36px);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,13 +67,53 @@
|
||||
.activitypub-embed-content .ap-preview {
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
margin: 1em 0 0;
|
||||
min-height: 64px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activitypub-embed-content .ap-preview img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activitypub-embed-content .ap-preview video,
|
||||
.activitypub-embed-content .ap-preview audio {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
|
||||
.activitypub-embed-content .ap-preview audio {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activitypub-embed-content .ap-preview.layout-1 {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
.activitypub-embed-content .ap-preview.layout-2 {
|
||||
aspect-ratio: auto;
|
||||
grid-template-rows: 1fr;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.activitypub-embed-content .ap-preview.layout-3 > img:first-child {
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
.activitypub-embed-content .ap-preview-text {
|
||||
@ -94,7 +134,9 @@
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 399px) {
|
||||
|
||||
.activitypub-embed-meta span.ap-stat {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@ -0,0 +1,201 @@
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 1em;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
margin: 1em;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
main p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
flex: 0 0 auto;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.columns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin: 0 auto;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex: 1;
|
||||
padding: 1em;
|
||||
max-width: 285px;
|
||||
}
|
||||
|
||||
.sidebar h1 {
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 0;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
background-color: #6364ff;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar ul li {
|
||||
padding: 5px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.sidebar input[type="search"],
|
||||
.sidebar textarea {
|
||||
background-color: #f6f6f6;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar > div,
|
||||
main address {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-bottom: 1em;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #ccc;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.webfinger {
|
||||
color: #ccc;
|
||||
font-size: 0.8em;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
main address .name,
|
||||
main address .webfinger {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
address img,
|
||||
.sidebar .fake-image {
|
||||
border-radius: 8px;
|
||||
margin-right: 1em;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
main article {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
main .content {
|
||||
margin: 1em 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
main .content h2 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
main .attachments {
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
margin: 1em 0;
|
||||
min-height: 64px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main .attachments.layout-1 {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
main .attachments.layout-2 {
|
||||
aspect-ratio: auto;
|
||||
grid-template-rows: 1fr;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
main .attachments img {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main .attachments.layout-3 > img:first-child {
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
main .attachments video,
|
||||
main .attachments audio {
|
||||
max-width: 100%;
|
||||
margin: 1em 0;
|
||||
display: block;
|
||||
grid-column: 1 / span 2;
|
||||
}
|
||||
|
||||
main .attachments audio {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main .tags a {
|
||||
background-color: #f6f6f6;
|
||||
border-radius: 4px;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
margin-right: 0.5em;
|
||||
padding: 0.5em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
main .tags a:hover {
|
||||
background-color: #e6e6e6;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
main .column-header {
|
||||
font-size: 1.5em;
|
||||
margin: 0;
|
||||
padding: 5px 10px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@ -0,0 +1,310 @@
|
||||
/* ActivityPub Welcome Page Styles */
|
||||
|
||||
.activitypub-welcome-container {
|
||||
max-width: 800px;
|
||||
margin: 40px auto;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.activitypub-welcome-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Progress Circle */
|
||||
.activitypub-progress-circle {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.activitypub-progress-circle-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #1e1e1e;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.activitypub-progress-ring {
|
||||
transform: rotate(-90deg);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.activitypub-progress-ring-bg {
|
||||
fill: none;
|
||||
stroke: #f0f0f1;
|
||||
stroke-width: 6;
|
||||
}
|
||||
|
||||
.activitypub-progress-ring-circle {
|
||||
fill: none;
|
||||
stroke: #2271b1;
|
||||
stroke-width: 6;
|
||||
stroke-linecap: round;
|
||||
transition: stroke-dashoffset 0.5s ease;
|
||||
}
|
||||
|
||||
.activitypub-welcome-title {
|
||||
font-size: 28px;
|
||||
margin: 20px 0 10px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.activitypub-welcome-subtitle {
|
||||
font-size: 16px;
|
||||
color: #646970;
|
||||
margin: 0 0 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* Steps Styles */
|
||||
.activitypub-onboarding-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: #f6f7f7;
|
||||
margin-bottom: 15px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.activitypub-onboarding-step:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.activitypub-onboarding-step:hover {
|
||||
background-color: #f0f0f1;
|
||||
}
|
||||
|
||||
.step-indicator {
|
||||
margin-right: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dashicons-warning {
|
||||
color: #dba617;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.step-text h3 {
|
||||
margin: 0 0 5px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.step-text p {
|
||||
margin: 0;
|
||||
color: #646970;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.step-action {
|
||||
flex-shrink: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.activitypub-step-completed {
|
||||
background-color: #f0f7ee;
|
||||
}
|
||||
|
||||
.activitypub-step-completed:hover {
|
||||
background-color: #e2f1dc;
|
||||
}
|
||||
|
||||
.activitypub-step-completed .step-text h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.activitypub-step-completed .step-text h3::after {
|
||||
content: ".";
|
||||
}
|
||||
|
||||
.activitypub-step-completed .step-text p,
|
||||
.activitypub-step-completed .step-action {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.activitypub-step-completed .step-icon {
|
||||
color: #008a20;
|
||||
}
|
||||
|
||||
.step-action .button {
|
||||
min-width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Profiles Section */
|
||||
.activitypub-profiles-section {
|
||||
margin-top: 40px;
|
||||
border-top: 1px solid #f0f0f1;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.profiles-description {
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
color: #1e1e1e;
|
||||
}
|
||||
|
||||
.activitypub-profiles-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.activitypub-profile-card {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.profile-card-header {
|
||||
background-color: #f0f0f1;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #c3c4c7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.profile-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.profile-icon .dashicons {
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line no-descending-specificity */
|
||||
.profile-card-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.profile-card-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.extra-field {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.extra-field label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
color: #646970;
|
||||
}
|
||||
|
||||
.extra-field input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
font-size: 13px;
|
||||
background-color: #f6f7f7;
|
||||
border: 1px solid #dcdcde;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.profile-description {
|
||||
font-size: 13px;
|
||||
color: #646970;
|
||||
margin: 15px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.profile-card-content .button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Footer Styles */
|
||||
.activitypub-welcome-footer {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.skip-steps-link {
|
||||
color: #2271b1;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.skip-steps-link:hover {
|
||||
color: #135e96;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media screen and (max-width: 782px) {
|
||||
|
||||
.activitypub-welcome-container {
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.step-action {
|
||||
margin-left: 0;
|
||||
margin-top: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step-action .button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.activitypub-profiles-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.activitypub-profile-card {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
BIN
wp-content/plugins/activitypub/assets/img/follow-me.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
wp-content/plugins/activitypub/assets/img/followers.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.7 KiB |
BIN
wp-content/plugins/activitypub/assets/img/reactions.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
wp-content/plugins/activitypub/assets/img/reply.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 8.1 KiB |
@ -17,5 +17,4 @@ jQuery( function( $ ) {
|
||||
$( '.activate-now' ).removeClass( 'thickbox open-plugin-details-modal' );
|
||||
}, 1200 );
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
@ -0,0 +1,364 @@
|
||||
/**
|
||||
* ActivityPub Connected Applications JavaScript.
|
||||
*
|
||||
* Handles registering OAuth clients, deleting clients, and revoking
|
||||
* OAuth tokens from the user profile, following the WordPress core
|
||||
* Application Passwords UI pattern.
|
||||
*/
|
||||
|
||||
/* global activitypubConnectedApps, jQuery, ClipboardJS */
|
||||
|
||||
( function( $ ) {
|
||||
var $section = $( '#activitypub-connected-apps-section' ),
|
||||
$newAppForm = $section.find( '.create-application-password' ),
|
||||
$newAppFields = $newAppForm.find( '.input' ),
|
||||
$newAppButton = $newAppForm.find( '.button' ),
|
||||
$appsWrapper = $section.find( '#activitypub-registered-apps-wrapper' ),
|
||||
$appsTbody = $section.find( '#activitypub-registered-apps-tbody' ),
|
||||
$tokensWrapper = $section.find( '.activitypub-connected-apps-list-table-wrapper' ),
|
||||
$tokensTbody = $section.find( '#activitypub-connected-apps-tbody' ),
|
||||
$revokeAll = $section.find( '#activitypub-revoke-all-tokens' ),
|
||||
$deleteAll = $section.find( '#activitypub-delete-all-clients' );
|
||||
|
||||
// Register a new application.
|
||||
$newAppButton.on( 'click', function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
if ( $newAppButton.prop( 'aria-disabled' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $name = $( '#activitypub-new-app-name' );
|
||||
var $redirectUri = $( '#activitypub-new-app-redirect-uri' );
|
||||
|
||||
if ( 0 === $name.val().trim().length ) {
|
||||
$name.trigger( 'focus' );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 0 === $redirectUri.val().trim().length ) {
|
||||
$redirectUri.trigger( 'focus' );
|
||||
return;
|
||||
}
|
||||
|
||||
clearNotices();
|
||||
$newAppButton.prop( 'aria-disabled', true ).addClass( 'disabled' );
|
||||
|
||||
$.ajax( {
|
||||
url: activitypubConnectedApps.ajaxUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'activitypub_register_oauth_client',
|
||||
name: $name.val().trim(),
|
||||
redirect_uri: $redirectUri.val().trim(),
|
||||
_wpnonce: activitypubConnectedApps.nonce
|
||||
}
|
||||
} ).always( function() {
|
||||
$newAppButton.removeProp( 'aria-disabled' ).removeClass( 'disabled' );
|
||||
} ).done( function( response ) {
|
||||
if ( ! response.success ) {
|
||||
addNotice(
|
||||
response.data && response.data.message ? response.data.message : activitypubConnectedApps.registerError,
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build credential notice (matches core's tmpl-new-application-password).
|
||||
var $notice = $( '<div></div>' )
|
||||
.attr( 'role', 'alert' )
|
||||
.attr( 'tabindex', '-1' )
|
||||
.addClass( 'notice notice-success is-dismissible new-application-password-notice' );
|
||||
|
||||
// Client ID row.
|
||||
var $clientIdRow = $( '<p></p>' ).addClass( 'application-password-display' )
|
||||
.append( $( '<label></label>' ).text( activitypubConnectedApps.clientIdLabel ) )
|
||||
.append( $( '<input>' ).attr( { type: 'text', readonly: 'readonly' } ).addClass( 'code' ).val( response.data.client_id ) )
|
||||
.append(
|
||||
$( '<button>' ).attr( 'type', 'button' ).addClass( 'button copy-button' )
|
||||
.attr( 'data-clipboard-text', response.data.client_id )
|
||||
.text( activitypubConnectedApps.copy )
|
||||
)
|
||||
.append( $( '<span>' ).addClass( 'success hidden' ).attr( 'aria-hidden', 'true' ).text( activitypubConnectedApps.copied ) );
|
||||
|
||||
$notice.append( $clientIdRow );
|
||||
|
||||
// Client Secret row (if present).
|
||||
if ( response.data.client_secret ) {
|
||||
var $secretRow = $( '<p></p>' ).addClass( 'application-password-display' )
|
||||
.append( $( '<label></label>' ).text( activitypubConnectedApps.clientSecretLabel ) )
|
||||
.append( $( '<input>' ).attr( { type: 'text', readonly: 'readonly' } ).addClass( 'code' ).val( response.data.client_secret ) )
|
||||
.append(
|
||||
$( '<button>' ).attr( 'type', 'button' ).addClass( 'button copy-button' )
|
||||
.attr( 'data-clipboard-text', response.data.client_secret )
|
||||
.text( activitypubConnectedApps.copy )
|
||||
)
|
||||
.append( $( '<span>' ).addClass( 'success hidden' ).attr( 'aria-hidden', 'true' ).text( activitypubConnectedApps.copied ) );
|
||||
|
||||
$notice.append( $secretRow );
|
||||
}
|
||||
|
||||
$notice.append( $( '<p></p>' ).text( activitypubConnectedApps.saveWarning ) );
|
||||
|
||||
// Dismiss button (matches core's tmpl-new-application-password).
|
||||
$notice.append(
|
||||
$( '<button>' ).attr( 'type', 'button' ).addClass( 'notice-dismiss' )
|
||||
.append( $( '<span>' ).addClass( 'screen-reader-text' ).text( activitypubConnectedApps.dismiss ) )
|
||||
);
|
||||
|
||||
// Insert after the form (not inside it), same as core.
|
||||
$newAppForm.after( $notice );
|
||||
$notice.trigger( 'focus' );
|
||||
|
||||
// Initialize ClipboardJS for the new notice.
|
||||
if ( 'undefined' !== typeof ClipboardJS ) {
|
||||
new ClipboardJS( '.new-application-password-notice .copy-button' )
|
||||
.on( 'success', function( clipEvent ) {
|
||||
var $btn = $( clipEvent.trigger );
|
||||
$btn.siblings( '.success' ).removeClass( 'hidden' );
|
||||
setTimeout( function() {
|
||||
$btn.siblings( '.success' ).addClass( 'hidden' );
|
||||
}, 3000 );
|
||||
} );
|
||||
}
|
||||
|
||||
// Add the new client to the registered apps table.
|
||||
var $row = $( '<tr>' )
|
||||
.attr( 'data-client-id', response.data.client_id )
|
||||
.append( $( '<td>' ).text( $name.val().trim() ) )
|
||||
.append( $( '<td>' ).text( $redirectUri.val().trim() ) )
|
||||
.append( $( '<td>' ).text( response.data.created ) )
|
||||
.append(
|
||||
$( '<td>' ).append(
|
||||
$( '<button>' )
|
||||
.addClass( 'button delete' )
|
||||
.text( activitypubConnectedApps.deleteLabel )
|
||||
)
|
||||
);
|
||||
|
||||
$appsTbody.prepend( $row );
|
||||
$appsWrapper.show();
|
||||
|
||||
// Clear the form fields.
|
||||
$name.val( '' );
|
||||
$redirectUri.val( '' );
|
||||
} ).fail( function( xhr, textStatus, errorThrown ) {
|
||||
var errorMessage = errorThrown;
|
||||
|
||||
if ( xhr.responseJSON && xhr.responseJSON.message ) {
|
||||
errorMessage = xhr.responseJSON.message;
|
||||
}
|
||||
|
||||
addNotice( errorMessage || activitypubConnectedApps.registerError, 'error' );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Delete a registered client.
|
||||
$appsTbody.on( 'click', '.delete', function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
if ( ! window.confirm( activitypubConnectedApps.confirmDelete ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $button = $( this ),
|
||||
$tr = $button.closest( 'tr' ),
|
||||
clientId = $tr.data( 'client-id' );
|
||||
|
||||
clearNotices();
|
||||
$button.prop( 'disabled', true );
|
||||
|
||||
$.ajax( {
|
||||
url: activitypubConnectedApps.ajaxUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'activitypub_delete_oauth_client',
|
||||
client_id: clientId,
|
||||
_wpnonce: activitypubConnectedApps.nonce
|
||||
}
|
||||
} ).always( function() {
|
||||
$button.prop( 'disabled', false );
|
||||
} ).done( function( response ) {
|
||||
if ( response.success && response.data.deleted ) {
|
||||
if ( 0 === $tr.siblings().length ) {
|
||||
$appsWrapper.hide();
|
||||
}
|
||||
$tr.remove();
|
||||
|
||||
addNotice( activitypubConnectedApps.appDeleted, 'success' ).trigger( 'focus' );
|
||||
}
|
||||
} ).fail( handleErrorResponse );
|
||||
} );
|
||||
|
||||
// Delete all registered clients.
|
||||
$deleteAll.on( 'click', function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
if ( ! window.confirm( activitypubConnectedApps.confirmDeleteAll ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $button = $( this );
|
||||
|
||||
clearNotices();
|
||||
$button.prop( 'disabled', true );
|
||||
|
||||
$.ajax( {
|
||||
url: activitypubConnectedApps.ajaxUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'activitypub_delete_all_oauth_clients',
|
||||
_wpnonce: activitypubConnectedApps.nonce
|
||||
}
|
||||
} ).always( function() {
|
||||
$button.prop( 'disabled', false );
|
||||
} ).done( function( response ) {
|
||||
if ( response.success && response.data.deleted ) {
|
||||
$appsTbody.children().remove();
|
||||
$appsWrapper.hide();
|
||||
|
||||
addNotice( activitypubConnectedApps.allAppsDeleted, 'success' ).trigger( 'focus' );
|
||||
}
|
||||
} ).fail( handleErrorResponse );
|
||||
} );
|
||||
|
||||
// Revoke a single token.
|
||||
$tokensTbody.on( 'click', '.delete', function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
if ( ! window.confirm( activitypubConnectedApps.confirm ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $button = $( this ),
|
||||
$tr = $button.closest( 'tr' ),
|
||||
metaKey = $tr.data( 'meta-key' );
|
||||
|
||||
clearNotices();
|
||||
$button.prop( 'disabled', true );
|
||||
|
||||
$.ajax( {
|
||||
url: activitypubConnectedApps.ajaxUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'activitypub_revoke_oauth_token',
|
||||
meta_key: metaKey,
|
||||
_wpnonce: activitypubConnectedApps.nonce
|
||||
}
|
||||
} ).always( function() {
|
||||
$button.prop( 'disabled', false );
|
||||
} ).done( function( response ) {
|
||||
if ( response.success && response.data.deleted ) {
|
||||
if ( 0 === $tr.siblings().length ) {
|
||||
$tokensWrapper.hide();
|
||||
}
|
||||
$tr.remove();
|
||||
|
||||
addNotice( activitypubConnectedApps.appRevoked, 'success' ).trigger( 'focus' );
|
||||
}
|
||||
} ).fail( handleErrorResponse );
|
||||
} );
|
||||
|
||||
// Revoke all tokens.
|
||||
$revokeAll.on( 'click', function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
if ( ! window.confirm( activitypubConnectedApps.confirmAll ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $button = $( this );
|
||||
|
||||
clearNotices();
|
||||
$button.prop( 'disabled', true );
|
||||
|
||||
$.ajax( {
|
||||
url: activitypubConnectedApps.ajaxUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'activitypub_revoke_all_oauth_tokens',
|
||||
_wpnonce: activitypubConnectedApps.nonce
|
||||
}
|
||||
} ).always( function() {
|
||||
$button.prop( 'disabled', false );
|
||||
} ).done( function( response ) {
|
||||
if ( response.success && response.data.deleted ) {
|
||||
$tokensTbody.children().remove();
|
||||
$section.children( '.new-application-password-notice' ).remove();
|
||||
$tokensWrapper.hide();
|
||||
|
||||
addNotice( activitypubConnectedApps.allAppsRevoked, 'success' ).trigger( 'focus' );
|
||||
}
|
||||
} ).fail( handleErrorResponse );
|
||||
} );
|
||||
|
||||
// Dismiss notices via event delegation on the section (same as core).
|
||||
$section.on( 'click', '.notice-dismiss', function( e ) {
|
||||
e.preventDefault();
|
||||
var $el = $( this ).parent();
|
||||
$el.removeAttr( 'role' );
|
||||
$el.fadeTo( 100, 0, function() {
|
||||
$el.slideUp( 100, function() {
|
||||
$el.remove();
|
||||
$newAppFields.first().trigger( 'focus' );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Submit form on Enter key in input fields (same as core).
|
||||
$newAppFields.on( 'keypress', function( e ) {
|
||||
if ( 13 === e.which ) {
|
||||
e.preventDefault();
|
||||
$newAppButton.trigger( 'click' );
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* Handles an error response from the AJAX call.
|
||||
*
|
||||
* @param {jqXHR} xhr The XHR object from the ajax call.
|
||||
* @param {string} textStatus The string categorizing the ajax request's status.
|
||||
* @param {string} errorThrown The HTTP status error text.
|
||||
*/
|
||||
function handleErrorResponse( xhr, textStatus, errorThrown ) {
|
||||
var errorMessage = errorThrown;
|
||||
|
||||
if ( xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message ) {
|
||||
errorMessage = xhr.responseJSON.data.message;
|
||||
}
|
||||
|
||||
addNotice( errorMessage, 'error' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notice message in the Connected Applications section.
|
||||
*
|
||||
* @param {string} message The message to display.
|
||||
* @param {string} type The notice type. Either 'success' or 'error'.
|
||||
* @returns {jQuery} The notice element.
|
||||
*/
|
||||
function addNotice( message, type ) {
|
||||
var $notice = $( '<div></div>' )
|
||||
.attr( 'role', 'alert' )
|
||||
.attr( 'tabindex', '-1' )
|
||||
.addClass( 'is-dismissible notice notice-' + type )
|
||||
.append( $( '<p></p>' ).text( message ) )
|
||||
.append(
|
||||
$( '<button></button>' )
|
||||
.attr( 'type', 'button' )
|
||||
.addClass( 'notice-dismiss' )
|
||||
.append( $( '<span></span>' ).addClass( 'screen-reader-text' ).text( activitypubConnectedApps.dismiss ) )
|
||||
);
|
||||
|
||||
$newAppForm.after( $notice );
|
||||
|
||||
return $notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears notice messages from the Connected Applications section.
|
||||
*/
|
||||
function clearNotices() {
|
||||
$( '.notice', $section ).remove();
|
||||
}
|
||||
}( jQuery ) );
|
||||
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* ActivityPub Following List Table Polling.
|
||||
*
|
||||
* Adds polling functionality to the Following list table to check for status updates
|
||||
* of pending follow requests without requiring manual page refresh.
|
||||
*
|
||||
* @package Activitypub
|
||||
*/
|
||||
|
||||
( function ( $ ) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Following List Table Polling.
|
||||
*/
|
||||
var ActivityPubFollowing = {
|
||||
/**
|
||||
* Initialize the polling functionality.
|
||||
*/
|
||||
init: function () {
|
||||
this.setupHeartbeatListeners();
|
||||
|
||||
// Check every 5 seconds. It'll automatically slow down after 2 mins 30 secs.
|
||||
window.wp.heartbeat.interval( 'fast' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Set up WordPress Heartbeat API listeners.
|
||||
*/
|
||||
setupHeartbeatListeners: function () {
|
||||
// Add our data to the Heartbeat API request.
|
||||
$( document ).on( 'heartbeat-send.activitypub_following', function ( e, data ) {
|
||||
data.activitypub_following_check = {
|
||||
user_id: ActivityPubFollowingSettings.user_id,
|
||||
pending_ids: ActivityPubFollowing.getPendingIds(),
|
||||
};
|
||||
} );
|
||||
|
||||
// Process the Heartbeat API response.
|
||||
$( document ).on( 'heartbeat-tick.activitypub_following', function ( e, data ) {
|
||||
if ( data.activitypub_following ) {
|
||||
ActivityPubFollowing.processUpdates( data.activitypub_following );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get IDs of all pending follow requests currently displayed in the table.
|
||||
*
|
||||
* @return {Array} Array of pending follow request IDs.
|
||||
*/
|
||||
getPendingIds: function () {
|
||||
var pendingIds = [];
|
||||
|
||||
// Find all rows with pending status.
|
||||
$( '.wp-list-table tr.status-pending' ).each( function () {
|
||||
var id = $( this ).attr( 'id' );
|
||||
|
||||
if ( id ) {
|
||||
// Extract the numeric ID from the row ID (e.g., "following-123" -> "123").
|
||||
pendingIds.push( id.replace( /^following-(\d+)$/, '$1' ) );
|
||||
}
|
||||
} );
|
||||
|
||||
return pendingIds;
|
||||
},
|
||||
|
||||
/**
|
||||
* Process updates received from the server.
|
||||
*
|
||||
* @param {Object} response Response data from the server.
|
||||
*/
|
||||
processUpdates: function ( response ) {
|
||||
if ( response.counts ) {
|
||||
// Update the counts in the views navigation.
|
||||
if ( Object.hasOwn( response.counts, 'all' ) ) {
|
||||
$( '.subsubsub .all .count' ).text( '(' + response.counts.all + ')' );
|
||||
}
|
||||
if ( Object.hasOwn( response.counts, 'accepted' ) ) {
|
||||
$( '.subsubsub .accepted .count' ).text( '(' + response.counts.accepted + ')' );
|
||||
}
|
||||
if ( Object.hasOwn( response.counts, 'pending' ) ) {
|
||||
$( '.subsubsub .pending .count' ).text( '(' + response.counts.pending + ')' );
|
||||
|
||||
// Remove heartbeat listeners when there are no more pending follows.
|
||||
if ( 0 === response.counts.pending ) {
|
||||
$( document ).off( 'heartbeat-send.activitypub_following' );
|
||||
$( document ).off( 'heartbeat-tick.activitypub_following' );
|
||||
window.wp.heartbeat.interval( 60 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! response.updated_items || ! response.updated_items.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any existing notices.
|
||||
$( 'div.notice' ).remove();
|
||||
|
||||
var $listTable = $( '#the-list' );
|
||||
|
||||
// Process each updated item.
|
||||
$.each( response.updated_items, function ( index, item ) {
|
||||
var $row = $( '#following-' + item.id );
|
||||
|
||||
if ( $row.length && item.status === 'accepted' ) {
|
||||
// Remove the row when we're in the "Pending" view.
|
||||
if ( 'pending' === new URLSearchParams( window.location.search ).get( 'status' ) ) {
|
||||
$row.remove();
|
||||
} else {
|
||||
$row.find( 'strong.pending' ).remove();
|
||||
}
|
||||
|
||||
if ( 0 === $listTable.children().length ) {
|
||||
$listTable.append(
|
||||
'<tr class="no-items"><td class="colspanchange" colspan="5">' + response.no_items + '</td></tr>'
|
||||
);
|
||||
}
|
||||
}
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
// Initialize on document ready.
|
||||
$( document ).ready( function () {
|
||||
ActivityPubFollowing.init();
|
||||
} );
|
||||
} )( jQuery );
|
||||
@ -0,0 +1,440 @@
|
||||
/**
|
||||
* ActivityPub Moderation Admin JavaScript
|
||||
*/
|
||||
|
||||
/* global activitypubModerationL10n, jQuery */
|
||||
|
||||
/**
|
||||
* @param {Object} $ - jQuery
|
||||
* @param {Object} wp - WordPress global object
|
||||
* @param {Object} wp.i18n - Internationalization functions
|
||||
* @param {Object} wp.a11y - Accessibility functions
|
||||
* @param {Object} wp.ajax - AJAX functions
|
||||
*/
|
||||
(function( $, wp ) {
|
||||
'use strict';
|
||||
|
||||
var __ = wp.i18n.__;
|
||||
var _n = wp.i18n._n;
|
||||
var sprintf = wp.i18n.sprintf;
|
||||
|
||||
/**
|
||||
* Helper function to show a message using wp.a11y and alert
|
||||
*
|
||||
* @param {string} message - The message to display
|
||||
*/
|
||||
function showMessage( message ) {
|
||||
if ( wp.a11y && wp.a11y.speak ) {
|
||||
wp.a11y.speak( message, 'assertive' );
|
||||
}
|
||||
alert( message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to validate domain format
|
||||
*
|
||||
* @param {string} domain - The domain to validate
|
||||
* @return {boolean} Whether the domain is valid
|
||||
*/
|
||||
function isValidDomain( domain ) {
|
||||
// Basic domain validation - must contain at least one dot and valid characters
|
||||
var domainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
return domainRegex.test( domain ) && domain.includes( '.' ) && domain.length > 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if a term already exists in the UI
|
||||
*
|
||||
* @param {string} type - The type of block (domain or keyword)
|
||||
* @param {string} value - The value to check
|
||||
* @param {string} context - The context (user or site)
|
||||
* @param {number|null} userId - The user ID (for user context)
|
||||
* @return {boolean} Whether the term is already blocked
|
||||
*/
|
||||
function isTermAlreadyBlocked( type, value, context, userId ) {
|
||||
var selector;
|
||||
if ( context === 'user' ) {
|
||||
selector = '.activitypub-user-block-list[data-user-id="' + userId + '"] .remove-user-block-btn[data-type="' + type + '"][data-value="' + value + '"]';
|
||||
} else if ( context === 'site' ) {
|
||||
selector = '.remove-site-block-btn[data-type="' + type + '"][data-value="' + value + '"]';
|
||||
}
|
||||
return $( selector ).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a blocked term value
|
||||
*
|
||||
* @param {string} type - The type of block (domain or keyword)
|
||||
* @param {string} value - The value to validate
|
||||
* @param {string} context - The context (user or site)
|
||||
* @param {number|null} userId - The user ID (for user context)
|
||||
* @return {boolean} Whether the value is valid
|
||||
*/
|
||||
function validateBlockedTerm( type, value, context, userId ) {
|
||||
if ( ! value ) {
|
||||
showMessage( __( 'Please enter a value to block.', 'activitypub' ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( type === 'domain' && ! isValidDomain( value ) ) {
|
||||
showMessage( __( 'Please enter a valid domain (e.g., example.com).', 'activitypub' ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isTermAlreadyBlocked( type, value, context, userId ) ) {
|
||||
showMessage( __( 'This term is already blocked.', 'activitypub' ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a table row for a blocked term.
|
||||
*
|
||||
* @param {string} type - The type of block (domain or keyword)
|
||||
* @param {string} value - The blocked value
|
||||
* @param {string} context - The context (user or site)
|
||||
* @return {jQuery} The constructed table row
|
||||
*/
|
||||
function createBlockedTermRow( type, value, context ) {
|
||||
var $button = $( '<button>', {
|
||||
type: 'button',
|
||||
'class': 'button button-small remove-' + context + '-block-btn',
|
||||
'data-type': type,
|
||||
'data-value': value,
|
||||
text: __( 'Remove', 'activitypub' )
|
||||
} );
|
||||
|
||||
return $( '<tr>' ).append( $( '<td>' ).text( value ), $( '<td>' ).append( $button ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to add a blocked term to the UI
|
||||
*/
|
||||
function addBlockedTermToUI( type, value, context, userId ) {
|
||||
var table;
|
||||
|
||||
if ( context === 'user' ) {
|
||||
// For user moderation, add to the appropriate table
|
||||
var container = $( '.activitypub-user-block-list[data-user-id="' + userId + '"]' );
|
||||
|
||||
table = container.find( '.activitypub-blocked-' + type );
|
||||
if ( table.length === 0 ) {
|
||||
table = $( '<table class="widefat striped activitypub-blocked-' + type + '" role="presentation" style="max-width: 500px; margin: 15px 0;"><tbody></tbody></table>' );
|
||||
container.find( '#new_user_' + type ).closest( '.add-user-block-form' ).before( table );
|
||||
}
|
||||
table.find( 'tbody' ).append( createBlockedTermRow( type, value, context ) );
|
||||
} else if ( context === 'site' ) {
|
||||
// For site moderation, add to the table inside the details element
|
||||
var details = $( '.activitypub-site-block-details[data-type="' + type + '"]' );
|
||||
table = details.find( '.activitypub-site-blocked-' + type );
|
||||
|
||||
if ( table.length === 0 ) {
|
||||
// Create table inside the details element (after summary)
|
||||
table = $( '<table class="widefat striped activitypub-site-blocked-' + type + '" role="presentation"><tbody></tbody></table>' );
|
||||
details.find( 'summary' ).after( table );
|
||||
}
|
||||
|
||||
table.find( 'tbody' ).append( createBlockedTermRow( type, value, context ) );
|
||||
|
||||
updateSiteBlockSummary( type );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to update the site block summary count
|
||||
*/
|
||||
function updateSiteBlockSummary( type ) {
|
||||
var details = $( '.activitypub-site-block-details[data-type="' + type + '"]' );
|
||||
var table = details.find( '.activitypub-site-blocked-' + type );
|
||||
var count = table.find( 'tbody tr' ).length || table.find( 'tr' ).length;
|
||||
var summary = details.find( 'summary' );
|
||||
|
||||
if ( count === 0 ) {
|
||||
// Empty state
|
||||
var emptyText = type === 'domain'
|
||||
? __( 'No blocked domains', 'activitypub' )
|
||||
: __( 'No blocked keywords', 'activitypub' );
|
||||
summary.text( emptyText );
|
||||
details.attr( 'open', '' );
|
||||
table.remove();
|
||||
} else {
|
||||
// Has items - use _n for proper pluralization
|
||||
var text = type === 'domain'
|
||||
? _n( '%s blocked domain', '%s blocked domains', count, 'activitypub' )
|
||||
: _n( '%s blocked keyword', '%s blocked keywords', count, 'activitypub' );
|
||||
summary.text( sprintf( text, count ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to remove a blocked term from the UI
|
||||
*/
|
||||
function removeBlockedTermFromUI( type, value, context ) {
|
||||
// Find and remove the specific blocked term element
|
||||
var selector = '.remove-' + context + '-block-btn[data-type="' + type + '"][data-value="' + value + '"]';
|
||||
var button = $( selector );
|
||||
|
||||
if ( button.length > 0 ) {
|
||||
// Remove the parent table row
|
||||
button.closest( 'tr' ).remove();
|
||||
|
||||
// Update the summary count for site blocks
|
||||
if ( context === 'site' ) {
|
||||
updateSiteBlockSummary( type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize moderation functionality
|
||||
*/
|
||||
function init() {
|
||||
// User moderation management.
|
||||
initUserModeration();
|
||||
|
||||
// Site moderation management.
|
||||
initSiteModeration();
|
||||
|
||||
// Blocklist subscriptions management.
|
||||
initBlocklistSubscriptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize user moderation management
|
||||
*/
|
||||
function initUserModeration() {
|
||||
// Function to add user blocked term.
|
||||
function addUserBlockedTerm( type, userId ) {
|
||||
var input = $( '#new_user_' + type );
|
||||
var value = input.val().trim();
|
||||
|
||||
if ( ! validateBlockedTerm( type, value, 'user', userId ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp.ajax.post( 'activitypub_moderation_settings', {
|
||||
context: 'user',
|
||||
operation: 'add',
|
||||
user_id: userId,
|
||||
type: type,
|
||||
value: value,
|
||||
_wpnonce: activitypubModerationL10n.nonce
|
||||
}).done( function() {
|
||||
// Clear input and add item to the UI.
|
||||
input.val( '' );
|
||||
addBlockedTermToUI( type, value, 'user', userId );
|
||||
}).fail( function( response ) {
|
||||
var message = response && response.message ? response.message : __( 'Failed to add block.', 'activitypub' );
|
||||
showMessage( message );
|
||||
});
|
||||
}
|
||||
|
||||
// Function to remove user blocked term.
|
||||
function removeUserBlockedTerm( type, value, userId ) {
|
||||
wp.ajax.post( 'activitypub_moderation_settings', {
|
||||
context: 'user',
|
||||
operation: 'remove',
|
||||
user_id: userId,
|
||||
type: type,
|
||||
value: value,
|
||||
_wpnonce: activitypubModerationL10n.nonce
|
||||
}).done( function() {
|
||||
removeBlockedTermFromUI( type, value, 'user' );
|
||||
}).fail( function( response ) {
|
||||
var message = response && response.message ? response.message : __( 'Failed to remove block.', 'activitypub' );
|
||||
showMessage( message );
|
||||
});
|
||||
}
|
||||
|
||||
// Add user block functionality (button click).
|
||||
$( document ).on( 'click', '.add-user-block-btn', function( e ) {
|
||||
e.preventDefault();
|
||||
var type = $( this ).data( 'type' );
|
||||
var userId = $( this ).closest( '.activitypub-user-block-list' ).data( 'user-id' );
|
||||
addUserBlockedTerm( type, userId );
|
||||
});
|
||||
|
||||
// Add user block functionality (Enter key).
|
||||
$( document ).on( 'keypress', '#new_user_domain, #new_user_keyword', function( e ) {
|
||||
if ( e.which === 13 ) { // Enter key.
|
||||
e.preventDefault();
|
||||
var inputId = $( this ).attr( 'id' );
|
||||
var type = inputId.replace( 'new_user_', '' );
|
||||
var userId = $( this ).closest( '.activitypub-user-block-list' ).data( 'user-id' );
|
||||
addUserBlockedTerm( type, userId );
|
||||
}
|
||||
});
|
||||
|
||||
// Remove user block functionality.
|
||||
$( document ).on( 'click', '.remove-user-block-btn', function( e ) {
|
||||
e.preventDefault();
|
||||
var type = $( this ).data( 'type' );
|
||||
var value = $( this ).data( 'value' );
|
||||
var userId = $( this ).closest( '.activitypub-user-block-list' ).data( 'user-id' );
|
||||
removeUserBlockedTerm( type, value, userId );
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize site moderation management
|
||||
*/
|
||||
function initSiteModeration() {
|
||||
// Function to add site blocked term.
|
||||
function addSiteBlockedTerm( type ) {
|
||||
var input = $( '#new_site_' + type );
|
||||
var value = input.val().trim();
|
||||
|
||||
if ( ! validateBlockedTerm( type, value, 'site', null ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp.ajax.post( 'activitypub_moderation_settings', {
|
||||
context: 'site',
|
||||
operation: 'add',
|
||||
type: type,
|
||||
value: value,
|
||||
_wpnonce: activitypubModerationL10n.nonce
|
||||
}).done( function() {
|
||||
// Clear input and add item to the UI.
|
||||
input.val( '' );
|
||||
addBlockedTermToUI( type, value, 'site' );
|
||||
}).fail( function( response ) {
|
||||
var message = response && response.message ? response.message : __( 'Failed to add block.', 'activitypub' );
|
||||
showMessage( message );
|
||||
});
|
||||
}
|
||||
|
||||
// Function to remove site blocked term.
|
||||
function removeSiteBlockedTerm( type, value ) {
|
||||
wp.ajax.post( 'activitypub_moderation_settings', {
|
||||
context: 'site',
|
||||
operation: 'remove',
|
||||
type: type,
|
||||
value: value,
|
||||
_wpnonce: activitypubModerationL10n.nonce
|
||||
}).done( function() {
|
||||
removeBlockedTermFromUI( type, value, 'site' );
|
||||
}).fail( function( response ) {
|
||||
var message = response && response.message ? response.message : __( 'Failed to remove block.', 'activitypub' );
|
||||
showMessage( message );
|
||||
});
|
||||
}
|
||||
|
||||
// Add site block functionality (button click).
|
||||
$( document ).on( 'click', '.add-site-block-btn', function( e ) {
|
||||
e.preventDefault();
|
||||
var type = $( this ).data( 'type' );
|
||||
addSiteBlockedTerm( type );
|
||||
});
|
||||
|
||||
// Add site block functionality (Enter key).
|
||||
$( document ).on( 'keypress', '#new_site_domain, #new_site_keyword', function( e ) {
|
||||
if ( e.which === 13 ) { // Enter key.
|
||||
e.preventDefault();
|
||||
var inputId = $( this ).attr( 'id' );
|
||||
var type = inputId.replace( 'new_site_', '' );
|
||||
addSiteBlockedTerm( type );
|
||||
}
|
||||
});
|
||||
|
||||
// Remove site block functionality.
|
||||
$( document ).on( 'click', '.remove-site-block-btn', function( e ) {
|
||||
e.preventDefault();
|
||||
var type = $( this ).data( 'type' );
|
||||
var value = $( this ).data( 'value' );
|
||||
removeSiteBlockedTerm( type, value );
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize blocklist subscriptions management
|
||||
*/
|
||||
function initBlocklistSubscriptions() {
|
||||
// Function to add a blocklist subscription.
|
||||
function addBlocklistSubscription( url ) {
|
||||
if ( ! url ) {
|
||||
var message = activitypubModerationL10n.enterUrl || 'Please enter a URL.';
|
||||
if ( wp.a11y && wp.a11y.speak ) {
|
||||
wp.a11y.speak( message, 'assertive' );
|
||||
}
|
||||
alert( message );
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the button while processing.
|
||||
var button = $( '.add-blocklist-subscription-btn' );
|
||||
button.prop( 'disabled', true );
|
||||
|
||||
wp.ajax.post( 'activitypub_blocklist_subscription', {
|
||||
operation: 'add',
|
||||
url: url,
|
||||
_wpnonce: activitypubModerationL10n.nonce
|
||||
}).done( function() {
|
||||
// Reload the page to show the updated list.
|
||||
window.location.reload();
|
||||
}).fail( function( response ) {
|
||||
var message = response && response.message ? response.message : activitypubModerationL10n.subscriptionFailed || 'Failed to add subscription.';
|
||||
if ( wp.a11y && wp.a11y.speak ) {
|
||||
wp.a11y.speak( message, 'assertive' );
|
||||
}
|
||||
alert( message );
|
||||
button.prop( 'disabled', false );
|
||||
});
|
||||
}
|
||||
|
||||
// Function to remove a blocklist subscription.
|
||||
function removeBlocklistSubscription( url ) {
|
||||
wp.ajax.post( 'activitypub_blocklist_subscription', {
|
||||
operation: 'remove',
|
||||
url: url,
|
||||
_wpnonce: activitypubModerationL10n.nonce
|
||||
}).done( function() {
|
||||
// Remove the row from the UI.
|
||||
$( '.remove-blocklist-subscription-btn' ).filter( function() {
|
||||
return $( this ).data( 'url' ) === url;
|
||||
}).closest( 'tr' ).remove();
|
||||
|
||||
// If no more subscriptions, remove the table.
|
||||
var table = $( '.activitypub-blocklist-subscriptions table' );
|
||||
if ( table.find( 'tbody tr' ).length === 0 ) {
|
||||
table.remove();
|
||||
}
|
||||
}).fail( function( response ) {
|
||||
var message = response && response.message ? response.message : activitypubModerationL10n.removeSubscriptionFailed || 'Failed to remove subscription.';
|
||||
if ( wp.a11y && wp.a11y.speak ) {
|
||||
wp.a11y.speak( message, 'assertive' );
|
||||
}
|
||||
alert( message );
|
||||
});
|
||||
}
|
||||
|
||||
// Add subscription functionality (button click).
|
||||
$( document ).on( 'click', '.add-blocklist-subscription-btn', function( e ) {
|
||||
e.preventDefault();
|
||||
var url = $( this ).data( 'url' ) || $( '#new_blocklist_subscription_url' ).val().trim();
|
||||
addBlocklistSubscription( url );
|
||||
});
|
||||
|
||||
// Add subscription functionality (Enter key).
|
||||
$( document ).on( 'keypress', '#new_blocklist_subscription_url', function( e ) {
|
||||
if ( e.which === 13 ) { // Enter key.
|
||||
e.preventDefault();
|
||||
var url = $( this ).val().trim();
|
||||
addBlocklistSubscription( url );
|
||||
}
|
||||
});
|
||||
|
||||
// Remove subscription functionality.
|
||||
$( document ).on( 'click', '.remove-blocklist-subscription-btn', function( e ) {
|
||||
e.preventDefault();
|
||||
var url = $( this ).data( 'url' );
|
||||
removeBlocklistSubscription( url );
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize when document is ready.
|
||||
$( document ).ready( init );
|
||||
|
||||
})( jQuery, wp );
|
||||