feat: Implement posts management functionality in the admin panel.
This commit is contained in:
BIN
admin/.DS_Store
vendored
BIN
admin/.DS_Store
vendored
Binary file not shown.
@@ -18,7 +18,7 @@
|
||||
--igsp-gray: #6c757d;
|
||||
--igsp-light: #f8f9fa;
|
||||
--igsp-border: #e2e4e7;
|
||||
--igsp-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
--igsp-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
--igsp-radius: 8px;
|
||||
}
|
||||
|
||||
@@ -414,8 +414,15 @@
|
||||
}
|
||||
|
||||
@keyframes igsp-progress-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.igsp-progress-text {
|
||||
@@ -515,8 +522,13 @@
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.igsp-log-type { width: 100px; }
|
||||
.igsp-log-date { width: 120px; }
|
||||
.igsp-log-type {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.igsp-log-date {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.igsp-log-badge {
|
||||
display: inline-block;
|
||||
@@ -527,10 +539,25 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.igsp-log-success { background: rgba(40, 167, 69, 0.1); color: var(--igsp-success); }
|
||||
.igsp-log-error { background: rgba(220, 53, 69, 0.1); color: var(--igsp-error); }
|
||||
.igsp-log-warning { background: rgba(255, 193, 7, 0.1); color: #856404; }
|
||||
.igsp-log-info { background: rgba(23, 162, 184, 0.1); color: var(--igsp-info); }
|
||||
.igsp-log-success {
|
||||
background: rgba(40, 167, 69, 0.1);
|
||||
color: var(--igsp-success);
|
||||
}
|
||||
|
||||
.igsp-log-error {
|
||||
background: rgba(220, 53, 69, 0.1);
|
||||
color: var(--igsp-error);
|
||||
}
|
||||
|
||||
.igsp-log-warning {
|
||||
background: rgba(255, 193, 7, 0.1);
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.igsp-log-info {
|
||||
background: rgba(23, 162, 184, 0.1);
|
||||
color: var(--igsp-info);
|
||||
}
|
||||
|
||||
.igsp-no-logs {
|
||||
padding: 40px;
|
||||
@@ -614,12 +641,12 @@
|
||||
.igsp-admin-wrap {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
|
||||
.igsp-admin-sidebar {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
|
||||
.igsp-sidebar-box {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
@@ -631,19 +658,456 @@
|
||||
flex-wrap: wrap;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
.igsp-tab-link {
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
.igsp-tab-link .dashicons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.igsp-layout-selector {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Posts Management Tab
|
||||
============================================ */
|
||||
|
||||
/* Posts Header */
|
||||
.igsp-posts-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.igsp-posts-header h2 {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.igsp-post-count {
|
||||
font-weight: 400;
|
||||
color: var(--igsp-gray);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
.igsp-search-wrapper {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.igsp-search-input {
|
||||
min-width: 220px;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--igsp-border);
|
||||
border-radius: var(--igsp-radius);
|
||||
}
|
||||
|
||||
/* Posts Table */
|
||||
.igsp-posts-table-wrapper {
|
||||
border: 1px solid var(--igsp-border);
|
||||
border-radius: var(--igsp-radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.igsp-posts-table {
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.igsp-posts-table th {
|
||||
background: var(--igsp-light);
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
.igsp-posts-table td {
|
||||
padding: 10px 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.igsp-col-thumb {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.igsp-col-type {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.igsp-col-status {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.igsp-col-link {
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.igsp-col-date {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.igsp-col-actions {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
/* Post Thumbnail */
|
||||
.igsp-post-thumb {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.igsp-post-thumb-placeholder {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: var(--igsp-light);
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--igsp-gray);
|
||||
}
|
||||
|
||||
.igsp-post-thumb-placeholder .dashicons {
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Caption */
|
||||
.igsp-caption-text {
|
||||
font-size: 13px;
|
||||
color: var(--igsp-dark);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Post Link */
|
||||
.igsp-post-link {
|
||||
color: var(--igsp-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.igsp-post-link:hover {
|
||||
color: var(--igsp-primary-hover);
|
||||
}
|
||||
|
||||
.igsp-post-link .dashicons {
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.igsp-no-link {
|
||||
color: var(--igsp-gray);
|
||||
}
|
||||
|
||||
/* Type Badges */
|
||||
.igsp-type-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.igsp-type-synced {
|
||||
background: rgba(23, 162, 184, 0.1);
|
||||
color: var(--igsp-info);
|
||||
}
|
||||
|
||||
.igsp-type-manual {
|
||||
background: rgba(225, 48, 108, 0.1);
|
||||
color: var(--igsp-primary);
|
||||
}
|
||||
|
||||
/* Status Badges */
|
||||
.igsp-status-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.igsp-status-active {
|
||||
background: rgba(40, 167, 69, 0.1);
|
||||
color: var(--igsp-success);
|
||||
}
|
||||
|
||||
.igsp-status-inactive {
|
||||
background: rgba(220, 53, 69, 0.1);
|
||||
color: var(--igsp-error);
|
||||
}
|
||||
|
||||
/* Inactive Row */
|
||||
.igsp-post-inactive {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.igsp-post-inactive:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Action Buttons */
|
||||
.igsp-action-buttons {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.igsp-action-buttons .button {
|
||||
padding: 2px 6px;
|
||||
min-height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.igsp-action-buttons .dashicons {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.igsp-delete-post {
|
||||
color: var(--igsp-error) !important;
|
||||
border-color: var(--igsp-error) !important;
|
||||
}
|
||||
|
||||
.igsp-delete-post:hover {
|
||||
background: var(--igsp-error) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* Post Date */
|
||||
.igsp-post-date {
|
||||
font-size: 12px;
|
||||
color: var(--igsp-gray);
|
||||
}
|
||||
|
||||
/* No Posts */
|
||||
.igsp-no-posts {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: var(--igsp-gray);
|
||||
background: var(--igsp-light);
|
||||
border-radius: var(--igsp-radius);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Pagination
|
||||
============================================ */
|
||||
.igsp-pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-top: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.igsp-pagination .button {
|
||||
min-width: 36px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.igsp-pagination-info {
|
||||
margin-left: 15px;
|
||||
font-size: 13px;
|
||||
color: var(--igsp-gray);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Manual Post Form
|
||||
============================================ */
|
||||
.igsp-manual-post-form {
|
||||
background: var(--igsp-light);
|
||||
border-radius: var(--igsp-radius);
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.igsp-manual-post-fields {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.igsp-manual-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.igsp-manual-field label {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: var(--igsp-dark);
|
||||
}
|
||||
|
||||
.igsp-manual-field .required {
|
||||
color: var(--igsp-error);
|
||||
}
|
||||
|
||||
.igsp-manual-image-field {
|
||||
grid-row: span 4;
|
||||
}
|
||||
|
||||
/* Image Upload Area */
|
||||
.igsp-image-upload-area {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border: 2px dashed var(--igsp-border);
|
||||
border-radius: var(--igsp-radius);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.igsp-image-upload-area:hover {
|
||||
border-color: var(--igsp-primary);
|
||||
}
|
||||
|
||||
.igsp-upload-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: 8px;
|
||||
color: var(--igsp-gray);
|
||||
}
|
||||
|
||||
.igsp-upload-placeholder .dashicons {
|
||||
font-size: 40px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: var(--igsp-border);
|
||||
}
|
||||
|
||||
.igsp-upload-placeholder span:last-child {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Select Image Button */
|
||||
#igsp-select-image-btn {
|
||||
margin-top: 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 200px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#igsp-select-image-btn .dashicons {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* Image Preview */
|
||||
.igsp-image-preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.igsp-image-preview img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.igsp-remove-image {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
background: rgba(220, 53, 69, 0.9);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.igsp-remove-image:hover {
|
||||
background: var(--igsp-error);
|
||||
}
|
||||
|
||||
.igsp-remove-image .dashicons {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* Manual Post Actions */
|
||||
.igsp-manual-post-actions {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.igsp-manual-post-actions .button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.igsp-manual-post-actions .dashicons {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 782px) {
|
||||
.igsp-manual-post-fields {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.igsp-manual-image-field {
|
||||
grid-row: auto;
|
||||
}
|
||||
|
||||
.igsp-image-upload-area {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.igsp-posts-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.igsp-col-date,
|
||||
.igsp-col-type {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -25,18 +25,27 @@
|
||||
this.initSyncButton();
|
||||
this.initToolButtons();
|
||||
this.initTabPersistence();
|
||||
this.initPostsTab();
|
||||
this.initManualPostForm();
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize color pickers
|
||||
*/
|
||||
initColorPickers: function () {
|
||||
$('.igsp-color-picker').wpColorPicker({
|
||||
change: function (event, ui) {
|
||||
// Update live preview
|
||||
IGSPAdmin.updatePreview();
|
||||
}
|
||||
});
|
||||
if ($('.igsp-color-picker').length === 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$('.igsp-color-picker').wpColorPicker({
|
||||
change: function (event, ui) {
|
||||
// Update live preview
|
||||
IGSPAdmin.updatePreview();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// Color picker not available on this tab
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -233,6 +242,210 @@
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize Posts Tab functionality
|
||||
*/
|
||||
initPostsTab: function () {
|
||||
// Toggle post active/inactive
|
||||
$(document).on('click', '.igsp-toggle-post', function () {
|
||||
const $button = $(this);
|
||||
const postId = $button.data('post-id');
|
||||
const $row = $button.closest('.igsp-post-row');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
|
||||
$.post(igspAdmin.ajaxUrl, {
|
||||
action: 'igsp_toggle_post',
|
||||
nonce: igspAdmin.nonce,
|
||||
post_id: postId
|
||||
}, function (response) {
|
||||
if (response.success) {
|
||||
const isActive = response.data.is_active;
|
||||
const $icon = $button.find('.dashicons');
|
||||
const $badge = $row.find('.igsp-status-badge');
|
||||
|
||||
if (isActive) {
|
||||
$row.removeClass('igsp-post-inactive');
|
||||
$icon.removeClass('dashicons-visibility').addClass('dashicons-hidden');
|
||||
$badge.removeClass('igsp-status-inactive').addClass('igsp-status-active').text('Aktiv');
|
||||
$button.attr('title', 'Deaktivieren');
|
||||
} else {
|
||||
$row.addClass('igsp-post-inactive');
|
||||
$icon.removeClass('dashicons-hidden').addClass('dashicons-visibility');
|
||||
$badge.removeClass('igsp-status-active').addClass('igsp-status-inactive').text('Inaktiv');
|
||||
$button.attr('title', 'Aktivieren');
|
||||
}
|
||||
}
|
||||
$button.prop('disabled', false);
|
||||
}).fail(function () {
|
||||
$button.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
// Delete post
|
||||
$(document).on('click', '.igsp-delete-post', function () {
|
||||
if (!confirm(igspAdmin.strings.confirmDeletePost)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $button = $(this);
|
||||
const postId = $button.data('post-id');
|
||||
const $row = $button.closest('.igsp-post-row');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
|
||||
$.post(igspAdmin.ajaxUrl, {
|
||||
action: 'igsp_delete_post',
|
||||
nonce: igspAdmin.nonce,
|
||||
post_id: postId
|
||||
}, function (response) {
|
||||
if (response.success) {
|
||||
$row.fadeOut(300, function () {
|
||||
$(this).remove();
|
||||
// Update count
|
||||
const $count = $('.igsp-post-count');
|
||||
const currentCount = parseInt($count.text().replace(/\D/g, '')) || 0;
|
||||
$count.text('(' + Math.max(0, currentCount - 1) + ')');
|
||||
});
|
||||
} else {
|
||||
$button.prop('disabled', false);
|
||||
}
|
||||
}).fail(function () {
|
||||
$button.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize Manual Post Form
|
||||
*/
|
||||
initManualPostForm: function () {
|
||||
// Only init if the button exists on this page
|
||||
if ($('#igsp-select-image-btn').length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mediaFrame = null;
|
||||
let selectedAttachmentId = 0;
|
||||
|
||||
// Open media library via button
|
||||
$('#igsp-select-image-btn').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Check if wp.media is available
|
||||
if (typeof wp === 'undefined' || typeof wp.media === 'undefined') {
|
||||
alert('WordPress Media Library ist nicht verfügbar. Bitte lade die Seite neu.');
|
||||
return;
|
||||
}
|
||||
|
||||
// If media frame already exists, reopen it
|
||||
if (mediaFrame) {
|
||||
mediaFrame.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create media frame
|
||||
mediaFrame = wp.media({
|
||||
title: igspAdmin.strings.selectImage,
|
||||
button: {
|
||||
text: igspAdmin.strings.useImage
|
||||
},
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
}
|
||||
});
|
||||
|
||||
// When image is selected
|
||||
mediaFrame.on('select', function () {
|
||||
const attachment = mediaFrame.state().get('selection').first().toJSON();
|
||||
|
||||
selectedAttachmentId = attachment.id;
|
||||
$('#igsp-attachment-id').val(attachment.id);
|
||||
|
||||
// Show preview
|
||||
const previewUrl = attachment.sizes && attachment.sizes.medium
|
||||
? attachment.sizes.medium.url
|
||||
: attachment.url;
|
||||
|
||||
$('#igsp-preview-img').attr('src', previewUrl);
|
||||
$('#igsp-image-preview').show();
|
||||
$('#igsp-upload-placeholder').hide();
|
||||
});
|
||||
|
||||
mediaFrame.open();
|
||||
});
|
||||
|
||||
// Remove image
|
||||
$('#igsp-remove-image').on('click', function (e) {
|
||||
e.stopPropagation();
|
||||
selectedAttachmentId = 0;
|
||||
$('#igsp-attachment-id').val('');
|
||||
$('#igsp-preview-img').attr('src', '');
|
||||
$('#igsp-image-preview').hide();
|
||||
$('#igsp-upload-placeholder').show();
|
||||
});
|
||||
|
||||
// Submit manual post
|
||||
$('#igsp-add-manual-post').on('click', function () {
|
||||
const $button = $(this);
|
||||
const $result = $('#igsp-manual-post-result');
|
||||
const attachmentId = $('#igsp-attachment-id').val();
|
||||
|
||||
if (!attachmentId) {
|
||||
$result.removeClass('success').addClass('error').text('Bitte wähle ein Bild aus.').show();
|
||||
return;
|
||||
}
|
||||
|
||||
$button.prop('disabled', true);
|
||||
$result.hide();
|
||||
|
||||
// Convert datetime-local to MySQL format
|
||||
let postedAt = $('#igsp-manual-date').val();
|
||||
if (postedAt) {
|
||||
postedAt = postedAt.replace('T', ' ') + ':00';
|
||||
}
|
||||
|
||||
$.post(igspAdmin.ajaxUrl, {
|
||||
action: 'igsp_add_manual_post',
|
||||
nonce: igspAdmin.nonce,
|
||||
attachment_id: attachmentId,
|
||||
post_url: $('#igsp-manual-url').val(),
|
||||
caption: $('#igsp-manual-caption').val(),
|
||||
posted_at: postedAt
|
||||
}, function (response) {
|
||||
$result.removeClass('success error');
|
||||
|
||||
if (response.success) {
|
||||
$result.addClass('success').text(response.data.message).show();
|
||||
|
||||
// Reset form
|
||||
selectedAttachmentId = 0;
|
||||
$('#igsp-attachment-id').val('');
|
||||
$('#igsp-preview-img').attr('src', '');
|
||||
$('#igsp-image-preview').hide();
|
||||
$('#igsp-upload-placeholder').show();
|
||||
$('#igsp-manual-url').val('');
|
||||
$('#igsp-manual-caption').val('');
|
||||
$('#igsp-manual-date').val('');
|
||||
|
||||
// Reload after short delay to show new post in table
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 1500);
|
||||
} else {
|
||||
$result.addClass('error').text(response.data.message).show();
|
||||
}
|
||||
|
||||
$button.prop('disabled', false);
|
||||
}).fail(function () {
|
||||
$result.addClass('error').text('Ein Fehler ist aufgetreten.').show();
|
||||
$button.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update sync status
|
||||
*/
|
||||
|
||||
438
admin/views/tab-posts.php
Normal file
438
admin/views/tab-posts.php
Normal file
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
/**
|
||||
* Posts Management Tab
|
||||
*
|
||||
* @package Instagram_Gallery_Sync_Pro
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$database = new IGSP_Database();
|
||||
$image_handler = new IGSP_Image_Handler();
|
||||
|
||||
// Pagination
|
||||
$per_page = 20;
|
||||
$current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
|
||||
$search = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : '';
|
||||
$offset = ($current_page - 1) * $per_page;
|
||||
|
||||
// Get posts
|
||||
$total_posts = $database->count_posts_filtered(array(
|
||||
'active' => null,
|
||||
'search' => $search,
|
||||
));
|
||||
|
||||
$posts = $database->get_posts(array(
|
||||
'limit' => $per_page,
|
||||
'offset' => $offset,
|
||||
'order' => 'newest',
|
||||
'active' => false, // Show all posts (active + inactive)
|
||||
'search' => $search,
|
||||
));
|
||||
|
||||
$total_pages = ceil($total_posts / $per_page);
|
||||
?>
|
||||
|
||||
<!-- Manual Post Form -->
|
||||
<div class="igsp-section">
|
||||
<h2>
|
||||
<?php esc_html_e('Post manuell hinzufügen', 'instagram-gallery-sync-pro'); ?>
|
||||
</h2>
|
||||
<p class="description">
|
||||
<?php esc_html_e('Füge manuell ein Bild/Post hinzu, z.B. wenn Instagram ein Rate-Limit verhängt hat.', 'instagram-gallery-sync-pro'); ?>
|
||||
</p>
|
||||
|
||||
<div class="igsp-manual-post-form" id="igsp-manual-post-form">
|
||||
<div class="igsp-manual-post-fields">
|
||||
<div class="igsp-manual-field igsp-manual-image-field">
|
||||
<label>
|
||||
<?php esc_html_e('Bild', 'instagram-gallery-sync-pro'); ?> <span class="required">*</span>
|
||||
</label>
|
||||
<div class="igsp-image-upload-area" id="igsp-image-upload-area">
|
||||
<div class="igsp-image-preview" id="igsp-image-preview" style="display: none;">
|
||||
<img src="" alt="" id="igsp-preview-img">
|
||||
<button type="button" class="igsp-remove-image" id="igsp-remove-image"
|
||||
title="<?php esc_attr_e('Bild entfernen', 'instagram-gallery-sync-pro'); ?>">
|
||||
<span class="dashicons dashicons-no-alt"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="igsp-upload-placeholder" id="igsp-upload-placeholder">
|
||||
<span class="dashicons dashicons-format-image"></span>
|
||||
<span><?php esc_html_e('Klicke auf den Button unten', 'instagram-gallery-sync-pro'); ?></span>
|
||||
</div>
|
||||
<input type="hidden" id="igsp-attachment-id" value="">
|
||||
</div>
|
||||
<button type="button" class="button" id="igsp-select-image-btn">
|
||||
<span class="dashicons dashicons-format-image"></span>
|
||||
<?php esc_html_e('Bild auswählen', 'instagram-gallery-sync-pro'); ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="igsp-manual-field">
|
||||
<label for="igsp-manual-url">
|
||||
<?php esc_html_e('Instagram URL', 'instagram-gallery-sync-pro'); ?>
|
||||
</label>
|
||||
<input type="url" id="igsp-manual-url" placeholder="https://www.instagram.com/p/..."
|
||||
class="regular-text">
|
||||
<p class="description">
|
||||
<?php esc_html_e('Link zum Instagram-Post (optional).', 'instagram-gallery-sync-pro'); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="igsp-manual-field">
|
||||
<label for="igsp-manual-caption">
|
||||
<?php esc_html_e('Caption', 'instagram-gallery-sync-pro'); ?>
|
||||
</label>
|
||||
<textarea id="igsp-manual-caption" rows="3" class="large-text"
|
||||
placeholder="<?php esc_attr_e('Beschreibung des Posts...', 'instagram-gallery-sync-pro'); ?>"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="igsp-manual-field">
|
||||
<label for="igsp-manual-date">
|
||||
<?php esc_html_e('Datum', 'instagram-gallery-sync-pro'); ?>
|
||||
</label>
|
||||
<input type="datetime-local" id="igsp-manual-date" class="regular-text">
|
||||
<p class="description">
|
||||
<?php esc_html_e('Veröffentlichungsdatum (optional, Standard: jetzt).', 'instagram-gallery-sync-pro'); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="igsp-manual-post-actions">
|
||||
<button type="button" id="igsp-add-manual-post" class="button button-primary">
|
||||
<span class="dashicons dashicons-plus-alt2"></span>
|
||||
<?php esc_html_e('Post hinzufügen', 'instagram-gallery-sync-pro'); ?>
|
||||
</button>
|
||||
<div id="igsp-manual-post-result" class="igsp-sync-result" style="display: none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Posts Table -->
|
||||
<div class="igsp-section">
|
||||
<div class="igsp-posts-header">
|
||||
<h2>
|
||||
<?php esc_html_e('Alle Posts', 'instagram-gallery-sync-pro'); ?>
|
||||
<span class="igsp-post-count">(
|
||||
<?php echo esc_html($total_posts); ?>)
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<form method="get" class="igsp-search-form">
|
||||
<input type="hidden" name="page" value="<?php echo esc_attr(IGSP_Admin::PAGE_SLUG); ?>">
|
||||
<input type="hidden" name="tab" value="posts">
|
||||
<div class="igsp-search-wrapper">
|
||||
<input type="search" name="s" value="<?php echo esc_attr($search); ?>"
|
||||
placeholder="<?php esc_attr_e('Posts durchsuchen...', 'instagram-gallery-sync-pro'); ?>"
|
||||
class="igsp-search-input">
|
||||
<button type="submit" class="button">
|
||||
<span class="dashicons dashicons-search"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if (empty($posts)): ?>
|
||||
<div class="igsp-no-posts">
|
||||
<?php if (!empty($search)): ?>
|
||||
<p>
|
||||
<?php printf(esc_html__('Keine Posts gefunden für "%s".', 'instagram-gallery-sync-pro'), esc_html($search)); ?>
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<p>
|
||||
<?php esc_html_e('Noch keine Posts vorhanden. Synchronisiere dein Instagram-Konto oder füge manuell Posts hinzu.', 'instagram-gallery-sync-pro'); ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="igsp-posts-table-wrapper">
|
||||
<table class="igsp-posts-table widefat striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="igsp-col-thumb">
|
||||
<?php esc_html_e('Bild', 'instagram-gallery-sync-pro'); ?>
|
||||
</th>
|
||||
<th class="igsp-col-caption">
|
||||
<?php esc_html_e('Caption', 'instagram-gallery-sync-pro'); ?>
|
||||
</th>
|
||||
<th class="igsp-col-link">
|
||||
<?php esc_html_e('Link', 'instagram-gallery-sync-pro'); ?>
|
||||
</th>
|
||||
<th class="igsp-col-type">
|
||||
<?php esc_html_e('Typ', 'instagram-gallery-sync-pro'); ?>
|
||||
</th>
|
||||
<th class="igsp-col-status">
|
||||
<?php esc_html_e('Status', 'instagram-gallery-sync-pro'); ?>
|
||||
</th>
|
||||
<th class="igsp-col-date">
|
||||
<?php esc_html_e('Datum', 'instagram-gallery-sync-pro'); ?>
|
||||
</th>
|
||||
<th class="igsp-col-actions">
|
||||
<?php esc_html_e('Aktionen', 'instagram-gallery-sync-pro'); ?>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($posts as $post):
|
||||
// Get image URL
|
||||
$thumb_url = '';
|
||||
if (!empty($post->image_thumbnail_path)) {
|
||||
$thumb_url = $image_handler->get_image_url($post->image_thumbnail_path);
|
||||
} elseif (!empty($post->image_local_path)) {
|
||||
$thumb_url = $image_handler->get_image_url($post->image_local_path);
|
||||
}
|
||||
|
||||
$is_manual = !empty($post->is_manual);
|
||||
$is_active = !empty($post->is_active);
|
||||
$caption_short = !empty($post->caption) ? wp_trim_words($post->caption, 12, '...') : '—';
|
||||
$posted_date = !empty($post->posted_at) ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($post->posted_at)) : '—';
|
||||
?>
|
||||
<tr class="igsp-post-row <?php echo !$is_active ? 'igsp-post-inactive' : ''; ?>"
|
||||
data-post-id="<?php echo esc_attr($post->id); ?>">
|
||||
<td class="igsp-col-thumb">
|
||||
<?php if ($thumb_url): ?>
|
||||
<img src="<?php echo esc_url($thumb_url); ?>" alt="" class="igsp-post-thumb">
|
||||
<?php else: ?>
|
||||
<div class="igsp-post-thumb-placeholder">
|
||||
<span class="dashicons dashicons-format-image"></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="igsp-col-caption">
|
||||
<span class="igsp-caption-text">
|
||||
<?php echo esc_html($caption_short); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="igsp-col-link">
|
||||
<?php if (!empty($post->post_url)): ?>
|
||||
<a href="<?php echo esc_url($post->post_url); ?>" target="_blank" rel="noopener noreferrer"
|
||||
class="igsp-post-link" title="<?php echo esc_attr($post->post_url); ?>">
|
||||
<span class="dashicons dashicons-external"></span>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="igsp-no-link">—</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="igsp-col-type">
|
||||
<?php if ($is_manual): ?>
|
||||
<span class="igsp-type-badge igsp-type-manual">
|
||||
<?php esc_html_e('Manual', 'instagram-gallery-sync-pro'); ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="igsp-type-badge igsp-type-synced">
|
||||
<?php esc_html_e('Synced', 'instagram-gallery-sync-pro'); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="igsp-col-status">
|
||||
<span
|
||||
class="igsp-status-badge <?php echo $is_active ? 'igsp-status-active' : 'igsp-status-inactive'; ?>">
|
||||
<?php echo $is_active ? esc_html__('Aktiv', 'instagram-gallery-sync-pro') : esc_html__('Inaktiv', 'instagram-gallery-sync-pro'); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="igsp-col-date">
|
||||
<span class="igsp-post-date">
|
||||
<?php echo esc_html($posted_date); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="igsp-col-actions">
|
||||
<div class="igsp-action-buttons">
|
||||
<button type="button" class="button button-small igsp-toggle-post"
|
||||
data-post-id="<?php echo esc_attr($post->id); ?>"
|
||||
title="<?php echo $is_active ? esc_attr__('Deaktivieren', 'instagram-gallery-sync-pro') : esc_attr__('Aktivieren', 'instagram-gallery-sync-pro'); ?>">
|
||||
<span
|
||||
class="dashicons <?php echo $is_active ? 'dashicons-hidden' : 'dashicons-visibility'; ?>"></span>
|
||||
</button>
|
||||
<button type="button" class="button button-small igsp-delete-post"
|
||||
data-post-id="<?php echo esc_attr($post->id); ?>"
|
||||
title="<?php esc_attr_e('Löschen', 'instagram-gallery-sync-pro'); ?>">
|
||||
<span class="dashicons dashicons-trash"></span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php if ($total_pages > 1): ?>
|
||||
<div class="igsp-pagination">
|
||||
<?php
|
||||
$base_url = admin_url('admin.php?page=' . IGSP_Admin::PAGE_SLUG . '&tab=posts');
|
||||
if (!empty($search)) {
|
||||
$base_url .= '&s=' . urlencode($search);
|
||||
}
|
||||
|
||||
for ($i = 1; $i <= $total_pages; $i++):
|
||||
$page_url = $base_url . '&paged=' . $i;
|
||||
?>
|
||||
<a href="<?php echo esc_url($page_url); ?>"
|
||||
class="button <?php echo $current_page === $i ? 'button-primary' : ''; ?>">
|
||||
<?php echo esc_html($i); ?>
|
||||
</a>
|
||||
<?php endfor; ?>
|
||||
|
||||
<span class="igsp-pagination-info">
|
||||
<?php printf(
|
||||
esc_html__('Seite %1$d von %2$d (%3$d Posts)', 'instagram-gallery-sync-pro'),
|
||||
$current_page,
|
||||
$total_pages,
|
||||
$total_posts
|
||||
); ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(document).ready(function ($) {
|
||||
// === Media Library Image Upload ===
|
||||
var igspMediaFrame = null;
|
||||
|
||||
$('#igsp-select-image-btn').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (typeof wp === 'undefined' || typeof wp.media !== 'function') {
|
||||
alert('WordPress Media Library konnte nicht geladen werden. Bitte lade die Seite neu.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (igspMediaFrame) {
|
||||
igspMediaFrame.open();
|
||||
return;
|
||||
}
|
||||
|
||||
igspMediaFrame = wp.media({
|
||||
title: '<?php echo esc_js(__('Bild auswählen', 'instagram-gallery-sync-pro')); ?>',
|
||||
button: { text: '<?php echo esc_js(__('Bild verwenden', 'instagram-gallery-sync-pro')); ?>' },
|
||||
multiple: false,
|
||||
library: { type: 'image' }
|
||||
});
|
||||
|
||||
igspMediaFrame.on('select', function () {
|
||||
var attachment = igspMediaFrame.state().get('selection').first().toJSON();
|
||||
$('#igsp-attachment-id').val(attachment.id);
|
||||
var previewUrl = (attachment.sizes && attachment.sizes.medium) ? attachment.sizes.medium.url : attachment.url;
|
||||
$('#igsp-preview-img').attr('src', previewUrl);
|
||||
$('#igsp-image-preview').show();
|
||||
$('#igsp-upload-placeholder').hide();
|
||||
});
|
||||
|
||||
igspMediaFrame.open();
|
||||
});
|
||||
|
||||
// Remove image
|
||||
$('#igsp-remove-image').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$('#igsp-attachment-id').val('');
|
||||
$('#igsp-preview-img').attr('src', '');
|
||||
$('#igsp-image-preview').hide();
|
||||
$('#igsp-upload-placeholder').show();
|
||||
});
|
||||
|
||||
// === Submit Manual Post ===
|
||||
$('#igsp-add-manual-post').on('click', function () {
|
||||
var $btn = $(this);
|
||||
var $result = $('#igsp-manual-post-result');
|
||||
var attachmentId = $('#igsp-attachment-id').val();
|
||||
|
||||
if (!attachmentId) {
|
||||
$result.removeClass('success').addClass('error').text('Bitte wähle ein Bild aus.').show();
|
||||
return;
|
||||
}
|
||||
|
||||
$btn.prop('disabled', true);
|
||||
$result.hide();
|
||||
|
||||
var postedAt = $('#igsp-manual-date').val();
|
||||
if (postedAt) {
|
||||
postedAt = postedAt.replace('T', ' ') + ':00';
|
||||
}
|
||||
|
||||
$.post(igspAdmin.ajaxUrl, {
|
||||
action: 'igsp_add_manual_post',
|
||||
nonce: igspAdmin.nonce,
|
||||
attachment_id: attachmentId,
|
||||
post_url: $('#igsp-manual-url').val(),
|
||||
caption: $('#igsp-manual-caption').val(),
|
||||
posted_at: postedAt
|
||||
}, function (response) {
|
||||
$result.removeClass('success error');
|
||||
if (response.success) {
|
||||
$result.addClass('success').text(response.data.message).show();
|
||||
// Reset form
|
||||
$('#igsp-attachment-id').val('');
|
||||
$('#igsp-preview-img').attr('src', '');
|
||||
$('#igsp-image-preview').hide();
|
||||
$('#igsp-upload-placeholder').show();
|
||||
$('#igsp-manual-url').val('');
|
||||
$('#igsp-manual-caption').val('');
|
||||
$('#igsp-manual-date').val('');
|
||||
setTimeout(function () { location.reload(); }, 1500);
|
||||
} else {
|
||||
$result.addClass('error').text(response.data.message).show();
|
||||
}
|
||||
$btn.prop('disabled', false);
|
||||
}).fail(function () {
|
||||
$result.addClass('error').text('Ein Fehler ist aufgetreten.').show();
|
||||
$btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
// === Toggle Post Status ===
|
||||
$(document).on('click', '.igsp-toggle-post', function () {
|
||||
var $btn = $(this);
|
||||
var postId = $btn.data('post-id');
|
||||
var $row = $btn.closest('.igsp-post-row');
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
$.post(igspAdmin.ajaxUrl, {
|
||||
action: 'igsp_toggle_post',
|
||||
nonce: igspAdmin.nonce,
|
||||
post_id: postId
|
||||
}, function (response) {
|
||||
if (response.success) {
|
||||
var $icon = $btn.find('.dashicons');
|
||||
var $badge = $row.find('.igsp-status-badge');
|
||||
if (response.data.is_active) {
|
||||
$row.removeClass('igsp-post-inactive');
|
||||
$icon.removeClass('dashicons-visibility').addClass('dashicons-hidden');
|
||||
$badge.removeClass('igsp-status-inactive').addClass('igsp-status-active').text('Aktiv');
|
||||
} else {
|
||||
$row.addClass('igsp-post-inactive');
|
||||
$icon.removeClass('dashicons-hidden').addClass('dashicons-visibility');
|
||||
$badge.removeClass('igsp-status-active').addClass('igsp-status-inactive').text('Inaktiv');
|
||||
}
|
||||
}
|
||||
$btn.prop('disabled', false);
|
||||
}).fail(function () { $btn.prop('disabled', false); });
|
||||
});
|
||||
|
||||
// === Delete Post ===
|
||||
$(document).on('click', '.igsp-delete-post', function () {
|
||||
if (!confirm('Diesen Post wirklich löschen?')) return;
|
||||
var $btn = $(this);
|
||||
var postId = $btn.data('post-id');
|
||||
var $row = $btn.closest('.igsp-post-row');
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
$.post(igspAdmin.ajaxUrl, {
|
||||
action: 'igsp_delete_post',
|
||||
nonce: igspAdmin.nonce,
|
||||
post_id: postId
|
||||
}, function (response) {
|
||||
if (response.success) {
|
||||
$row.fadeOut(300, function () { $(this).remove(); });
|
||||
} else {
|
||||
$btn.prop('disabled', false);
|
||||
}
|
||||
}).fail(function () { $btn.prop('disabled', false); });
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -47,6 +47,9 @@ class IGSP_Admin
|
||||
add_action('wp_ajax_igsp_reset_data', array($this, 'ajax_reset_data'));
|
||||
add_action('wp_ajax_igsp_get_sync_status', array($this, 'ajax_get_sync_status'));
|
||||
add_action('wp_ajax_igsp_clear_logs', array($this, 'ajax_clear_logs'));
|
||||
add_action('wp_ajax_igsp_delete_post', array($this, 'ajax_delete_post'));
|
||||
add_action('wp_ajax_igsp_toggle_post', array($this, 'ajax_toggle_post'));
|
||||
add_action('wp_ajax_igsp_add_manual_post', array($this, 'ajax_add_manual_post'));
|
||||
|
||||
// Add settings link to plugins page
|
||||
add_filter('plugin_action_links_' . IGSP_PLUGIN_BASENAME, array($this, 'add_settings_link'));
|
||||
@@ -173,18 +176,21 @@ class IGSP_Admin
|
||||
'igsp-admin-style',
|
||||
IGSP_PLUGIN_URL . 'admin/css/admin-style.css',
|
||||
array(),
|
||||
IGSP_VERSION
|
||||
IGSP_VERSION . '.' . time()
|
||||
);
|
||||
|
||||
// WordPress components
|
||||
wp_enqueue_style('wp-color-picker');
|
||||
|
||||
// WordPress Media Library
|
||||
wp_enqueue_media();
|
||||
|
||||
// Admin JS
|
||||
wp_enqueue_script(
|
||||
'igsp-admin-script',
|
||||
IGSP_PLUGIN_URL . 'admin/js/admin-script.js',
|
||||
array('jquery', 'wp-color-picker'),
|
||||
IGSP_VERSION,
|
||||
array('jquery', 'wp-color-picker', 'media-upload'),
|
||||
IGSP_VERSION . '.' . time(),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -193,7 +199,7 @@ class IGSP_Admin
|
||||
'igsp-ajax-sync',
|
||||
IGSP_PLUGIN_URL . 'admin/js/ajax-sync.js',
|
||||
array('jquery'),
|
||||
IGSP_VERSION,
|
||||
IGSP_VERSION . '.' . time(),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -207,8 +213,13 @@ class IGSP_Admin
|
||||
'syncError' => __('Sync failed. Check logs for details.', 'instagram-gallery-sync-pro'),
|
||||
'confirmReset' => __('Are you sure? This will delete ALL Instagram data including images. This action cannot be undone.', 'instagram-gallery-sync-pro'),
|
||||
'confirmClearLogs' => __('Are you sure you want to clear all logs?', 'instagram-gallery-sync-pro'),
|
||||
'confirmDeletePost' => __('Are you sure you want to delete this post?', 'instagram-gallery-sync-pro'),
|
||||
'processing' => __('Processing...', 'instagram-gallery-sync-pro'),
|
||||
'done' => __('Done!', 'instagram-gallery-sync-pro'),
|
||||
'postAdded' => __('Post added successfully!', 'instagram-gallery-sync-pro'),
|
||||
'postDeleted' => __('Post deleted.', 'instagram-gallery-sync-pro'),
|
||||
'selectImage' => __('Select Image', 'instagram-gallery-sync-pro'),
|
||||
'useImage' => __('Use this image', 'instagram-gallery-sync-pro'),
|
||||
),
|
||||
));
|
||||
}
|
||||
@@ -382,6 +393,10 @@ class IGSP_Admin
|
||||
'title' => __('Instagram', 'instagram-gallery-sync-pro'),
|
||||
'icon' => 'dashicons-instagram',
|
||||
),
|
||||
'posts' => array(
|
||||
'title' => __('Posts', 'instagram-gallery-sync-pro'),
|
||||
'icon' => 'dashicons-images-alt2',
|
||||
),
|
||||
'layout' => array(
|
||||
'title' => __('Layout', 'instagram-gallery-sync-pro'),
|
||||
'icon' => 'dashicons-grid-view',
|
||||
@@ -400,4 +415,120 @@ class IGSP_Admin
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Delete single post
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_delete_post()
|
||||
{
|
||||
check_ajax_referer('igsp_admin_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Permission denied.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
|
||||
$post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
|
||||
|
||||
if (!$post_id) {
|
||||
wp_send_json_error(array('message' => __('Invalid post ID.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
|
||||
$database = new IGSP_Database();
|
||||
$result = $database->delete_post($post_id);
|
||||
|
||||
if ($result) {
|
||||
// Clear gallery cache
|
||||
igsp()->clear_transients();
|
||||
wp_send_json_success(array('message' => __('Post deleted successfully.', 'instagram-gallery-sync-pro')));
|
||||
} else {
|
||||
wp_send_json_error(array('message' => __('Failed to delete post.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Toggle post active/inactive
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_toggle_post()
|
||||
{
|
||||
check_ajax_referer('igsp_admin_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Permission denied.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
|
||||
$post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
|
||||
|
||||
if (!$post_id) {
|
||||
wp_send_json_error(array('message' => __('Invalid post ID.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
|
||||
$database = new IGSP_Database();
|
||||
$result = $database->toggle_post_active($post_id);
|
||||
|
||||
if ($result) {
|
||||
$post = $database->get_post($post_id);
|
||||
igsp()->clear_transients();
|
||||
wp_send_json_success(array(
|
||||
'message' => __('Post status updated.', 'instagram-gallery-sync-pro'),
|
||||
'is_active' => (bool) $post->is_active,
|
||||
));
|
||||
} else {
|
||||
wp_send_json_error(array('message' => __('Failed to update post status.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Add manual post
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_add_manual_post()
|
||||
{
|
||||
check_ajax_referer('igsp_admin_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Permission denied.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
|
||||
$attachment_id = isset($_POST['attachment_id']) ? absint($_POST['attachment_id']) : 0;
|
||||
$post_url = isset($_POST['post_url']) ? esc_url_raw($_POST['post_url']) : '';
|
||||
$caption = isset($_POST['caption']) ? wp_kses_post($_POST['caption']) : '';
|
||||
$posted_at = isset($_POST['posted_at']) ? sanitize_text_field($_POST['posted_at']) : '';
|
||||
|
||||
if (!$attachment_id) {
|
||||
wp_send_json_error(array('message' => __('Please select an image.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
|
||||
// Copy from Media Library to plugin upload directory
|
||||
$image_handler = new IGSP_Image_Handler();
|
||||
$image_result = $image_handler->save_from_media_library($attachment_id);
|
||||
|
||||
if (is_wp_error($image_result)) {
|
||||
wp_send_json_error(array('message' => $image_result->get_error_message()));
|
||||
}
|
||||
|
||||
// Insert into database
|
||||
$database = new IGSP_Database();
|
||||
$result = $database->insert_manual_post(array(
|
||||
'image_local_path' => $image_result['local_path'],
|
||||
'image_thumbnail_path' => $image_result['thumbnail_path'],
|
||||
'post_url' => $post_url,
|
||||
'caption' => $caption,
|
||||
'posted_at' => $posted_at,
|
||||
'file_size' => $image_result['file_size'],
|
||||
'image_width' => $image_result['width'],
|
||||
'image_height' => $image_result['height'],
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
igsp()->clear_transients();
|
||||
wp_send_json_success(array('message' => __('Post added successfully!', 'instagram-gallery-sync-pro')));
|
||||
} else {
|
||||
wp_send_json_error(array('message' => __('Failed to add post.', 'instagram-gallery-sync-pro')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class IGSP_Database
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $db_version = '1.0.0';
|
||||
private $db_version = '1.1.0';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -64,6 +64,7 @@ class IGSP_Database
|
||||
image_width INT(11) DEFAULT 0,
|
||||
image_height INT(11) DEFAULT 0,
|
||||
is_active TINYINT(1) DEFAULT 1,
|
||||
is_manual TINYINT(1) DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY instagram_id (instagram_id),
|
||||
KEY username (username),
|
||||
@@ -250,6 +251,7 @@ class IGSP_Database
|
||||
'order' => 'newest',
|
||||
'active' => true,
|
||||
'username' => '',
|
||||
'search' => '',
|
||||
);
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
@@ -267,6 +269,13 @@ class IGSP_Database
|
||||
$values[] = $args['username'];
|
||||
}
|
||||
|
||||
if (!empty($args['search'])) {
|
||||
$where[] = '(caption LIKE %s OR username LIKE %s)';
|
||||
$search_term = '%' . $wpdb->esc_like($args['search']) . '%';
|
||||
$values[] = $search_term;
|
||||
$values[] = $search_term;
|
||||
}
|
||||
|
||||
// Order
|
||||
switch ($args['order']) {
|
||||
case 'oldest':
|
||||
@@ -315,6 +324,116 @@ class IGSP_Database
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count posts with optional filters
|
||||
*
|
||||
* @param array $args Filter arguments
|
||||
* @return int
|
||||
*/
|
||||
public function count_posts_filtered($args = array())
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$defaults = array(
|
||||
'active' => null,
|
||||
'search' => '',
|
||||
);
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
|
||||
$where = array('1=1');
|
||||
$values = array();
|
||||
|
||||
if ($args['active'] !== null) {
|
||||
$where[] = 'is_active = %d';
|
||||
$values[] = $args['active'] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!empty($args['search'])) {
|
||||
$where[] = '(caption LIKE %s OR username LIKE %s)';
|
||||
$search_term = '%' . $wpdb->esc_like($args['search']) . '%';
|
||||
$values[] = $search_term;
|
||||
$values[] = $search_term;
|
||||
}
|
||||
|
||||
$where_clause = implode(' AND ', $where);
|
||||
$query = "SELECT COUNT(*) FROM " . IGSP_TABLE_POSTS . " WHERE {$where_clause}";
|
||||
|
||||
if (!empty($values)) {
|
||||
$query = $wpdb->prepare($query, $values);
|
||||
}
|
||||
|
||||
return (int) $wpdb->get_var($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle post active status
|
||||
*
|
||||
* @param int $id Post ID
|
||||
* @return bool
|
||||
*/
|
||||
public function toggle_post_active($id)
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$post = $this->get_post($id);
|
||||
if (!$post) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$new_status = $post->is_active ? 0 : 1;
|
||||
|
||||
return $wpdb->update(
|
||||
IGSP_TABLE_POSTS,
|
||||
array('is_active' => $new_status),
|
||||
array('id' => $id),
|
||||
array('%d'),
|
||||
array('%d')
|
||||
) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a manually added post
|
||||
*
|
||||
* @param array $data Post data
|
||||
* @return int|false Insert ID or false on failure
|
||||
*/
|
||||
public function insert_manual_post($data)
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$instagram_id = 'manual_' . time() . '_' . wp_rand(1000, 9999);
|
||||
|
||||
$insert_data = array(
|
||||
'instagram_id' => $instagram_id,
|
||||
'username' => sanitize_text_field($data['username'] ?? get_option('igsp_username', 'manual')),
|
||||
'image_local_path' => isset($data['image_local_path']) ? sanitize_text_field($data['image_local_path']) : '',
|
||||
'image_thumbnail_path' => isset($data['image_thumbnail_path']) ? sanitize_text_field($data['image_thumbnail_path']) : '',
|
||||
'post_url' => isset($data['post_url']) ? esc_url_raw($data['post_url']) : '',
|
||||
'caption' => isset($data['caption']) ? wp_kses_post($data['caption']) : '',
|
||||
'likes_count' => 0,
|
||||
'comments_count' => 0,
|
||||
'posted_at' => isset($data['posted_at']) && !empty($data['posted_at']) ? sanitize_text_field($data['posted_at']) : current_time('mysql'),
|
||||
'synced_at' => current_time('mysql'),
|
||||
'file_size' => isset($data['file_size']) ? absint($data['file_size']) : 0,
|
||||
'image_width' => isset($data['image_width']) ? absint($data['image_width']) : 0,
|
||||
'image_height' => isset($data['image_height']) ? absint($data['image_height']) : 0,
|
||||
'is_active' => 1,
|
||||
'is_manual' => 1,
|
||||
);
|
||||
|
||||
$result = $wpdb->insert(
|
||||
IGSP_TABLE_POSTS,
|
||||
$insert_data,
|
||||
array(
|
||||
'%s', '%s', '%s', '%s', '%s', '%s',
|
||||
'%d', '%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d'
|
||||
)
|
||||
);
|
||||
|
||||
return $result ? $wpdb->insert_id : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete post
|
||||
*
|
||||
|
||||
@@ -441,6 +441,66 @@ class IGSP_Image_Handler
|
||||
return size_format($bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save image from WordPress Media Library to plugin upload directory
|
||||
*
|
||||
* @param int $attachment_id WordPress attachment ID
|
||||
* @return array|WP_Error Array with paths or WP_Error
|
||||
*/
|
||||
public function save_from_media_library($attachment_id)
|
||||
{
|
||||
$file_path = get_attached_file($attachment_id);
|
||||
|
||||
if (!$file_path || !file_exists($file_path)) {
|
||||
return new WP_Error('file_not_found', __('Attachment file not found.', 'instagram-gallery-sync-pro'));
|
||||
}
|
||||
|
||||
// Validate
|
||||
$validation = $this->validate_image($file_path);
|
||||
if (is_wp_error($validation)) {
|
||||
return $validation;
|
||||
}
|
||||
|
||||
$file_info = $validation;
|
||||
$extension = $file_info['extension'];
|
||||
|
||||
// Generate unique filename
|
||||
$safe_id = 'manual_' . time() . '_' . wp_rand(1000, 9999);
|
||||
$filename = $safe_id . '.' . $extension;
|
||||
$dest_path = $this->upload_path . '/' . $filename;
|
||||
|
||||
// Copy file (don't move - keep original in Media Library)
|
||||
$copied = copy($file_path, $dest_path);
|
||||
|
||||
if (!$copied) {
|
||||
return new WP_Error('copy_failed', __('Failed to copy image file.', 'instagram-gallery-sync-pro'));
|
||||
}
|
||||
|
||||
chmod($dest_path, 0644);
|
||||
|
||||
// Generate thumbnail
|
||||
$thumbnail_path = $this->generate_thumbnail($dest_path, $safe_id, $extension);
|
||||
|
||||
// Get dimensions
|
||||
$dimensions = getimagesize($dest_path);
|
||||
|
||||
$result = array(
|
||||
'local_path' => IGSP_UPLOAD_DIR . '/' . $filename,
|
||||
'thumbnail_path' => $thumbnail_path ?: '',
|
||||
'full_url' => $this->upload_url . '/' . $filename,
|
||||
'file_size' => filesize($dest_path),
|
||||
'width' => $dimensions ? $dimensions[0] : 0,
|
||||
'height' => $dimensions ? $dimensions[1] : 0,
|
||||
);
|
||||
|
||||
$this->logger->info(
|
||||
sprintf(__('Manual image saved: %s', 'instagram-gallery-sync-pro'), $filename),
|
||||
array('size' => $result['file_size'])
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all stored images
|
||||
*
|
||||
|
||||
BIN
public/.DS_Store
vendored
BIN
public/.DS_Store
vendored
Binary file not shown.
Reference in New Issue
Block a user