PluginProbe
Metricool – Social media and site statistics / trunk
Metricool – Social media and site statistics vtrunk
2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
metricool / app / Controllers / SharePostController.php

SharePostController.php in Metricool – Social media and site statistics trunk, at app/Controllers/SharePostController.php

227 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Metricool\Controllers;
6
7 if (!defined('ABSPATH')) {
8 exit;
9 }
10
11 use Metricool\Support\Helpers\Event;
12 use Metricool\Traits\HasViews;
13 use Metricool\Traits\HasNonces;
14 use Metricool\Support\Helpers\MetricoolUrl;
15 use Metricool\Traits\HasAllowlistControl;
16 use Metricool\Interfaces\ControllerInterface;
17 use Metricool\Support\Helpers\Storages\RequestStorage;
18 use Metricool\Support\Helpers\Storages\EnvironmentConfig;
19
20 class SharePostController implements ControllerInterface
21 {
22 use HasAllowlistControl;
23 use HasViews;
24 use HasNonces;
25
26 private const DEFAULT_POST_TYPES = ['post', 'page'];
27 private const POST_COLUMN_KEY = 'metricool';
28 private const SHARE_POST_ACTION = 'share_post';
29
30 private EnvironmentConfig $env;
31 private RequestStorage $request;
32
33 public function __construct(EnvironmentConfig $env, RequestStorage $request)
34 {
35 $this->env = $env;
36 $this->request = $request;
37 }
38
39 public function register(): void
40 {
41 if ($this->userCanManage() === false) {
42 return;
43 }
44
45 add_action('init', [$this, 'registerSharePostColumn']);
46 add_action('admin_init', [$this, 'processShareAction']);
47 add_action('add_meta_boxes', [$this, 'addShareMetaBox']);
48 add_filter('default_hidden_columns', [$this, 'filterDefaultHiddenColumns'], 10, 2);
49 }
50
51 /**
52 * Registers the Metricool share post column on all public post types. Due
53 * to {@see filterDefaultHiddenColumns} the column will be hidden by default
54 * on all post types except the ones defined in {@see DEFAULT_POST_TYPES}.
55 */
56 public function registerSharePostColumn(): void
57 {
58 foreach ($this->getAllPublicPostTypes() as $postType) {
59 add_filter("manage_{$postType}_posts_columns", [$this, 'insertPostTableColumn']);
60 add_action("manage_{$postType}_posts_custom_column", [$this, 'insertPostTableColumnContent'], 10, 2);
61 }
62 }
63
64 /**
65 * Retrieves all public and viewable post types.
66 */
67 private function getAllPublicPostTypes(): array
68 {
69 $postTypes = get_post_types(['public' => true]);
70 return array_filter($postTypes, 'is_post_type_viewable');
71 }
72
73 /**
74 * Method is used for hiding the custom column by default on all post type
75 * list table screens except the configured default post types. Users can
76 * still enable the column manually through Screen Options.
77 */
78 public function filterDefaultHiddenColumns(array $hiddenColumns, \WP_Screen $currentScreen): array
79 {
80 // Only act on post type list tables like edit-post, edit-page, etc.
81 if (strpos($currentScreen->id, 'edit-') !== 0) {
82 return $hiddenColumns;
83 }
84
85 if (empty($currentScreen->post_type)) {
86 return $hiddenColumns;
87 }
88
89 // Keep column visible if post type is in the default list.
90 if (in_array($currentScreen->post_type, self::DEFAULT_POST_TYPES, true)) {
91 return $hiddenColumns;
92 }
93
94 // Hide the POST_COLUMN_KEY column by default if not already hidden.
95 if (!in_array(self::POST_COLUMN_KEY, $hiddenColumns, true)) {
96 $hiddenColumns[] = self::POST_COLUMN_KEY;
97 }
98
99 return $hiddenColumns;
100 }
101
102 /**
103 * Handles known actions from the {@see Storage} based on the
104 * metricool_action key.
105 */
106 public function processShareAction(): void
107 {
108 $pageIsDashboardPage = ($this->request->getString('global.page') === 'metricool');
109 $actionIsShareAction = ($this->request->getString('global.metricool_action') === self::SHARE_POST_ACTION);
110
111 if (!$pageIsDashboardPage || !$actionIsShareAction) {
112 return; // abort
113 }
114
115 // Validate nonce or wp_die on empty
116 $nonce = $this->request->getString('global._metricool_action_nonce');
117 if (empty($nonce) || !$this->verifyNonce($nonce, 'metricool_action')) {
118 wp_die(esc_html__('Invalid nonce.', 'metricool'));
119 }
120
121 $this->handleSharePostAction();
122 }
123
124 /**
125 * Sets the metricool column header to the post tables
126 */
127 public function insertPostTableColumn(array $columns): array
128 {
129 $columns[self::POST_COLUMN_KEY] = $this->view('admin/share-post/column-header', [
130 'label' => __('Metricool', 'metricool')
131 ]);
132 return $columns;
133 }
134
135 /**
136 * Sets the content of the metricool share post column
137 */
138 public function insertPostTableColumnContent(string $columnName, int $postId): void
139 {
140 if (($columnName !== self::POST_COLUMN_KEY) || (get_post_status($postId) !== 'publish')) {
141 return;
142 }
143
144 $this->render('admin/share-post/button', $this->getArgumentsToSharePost($postId));
145 }
146
147 /**
148 * Redirects to the Metricool create post screen with the content and media
149 */
150 protected function handleSharePostAction(): void
151 {
152 $postId = $this->request->getInt('global.metricool_post_id');
153 $postExists = (get_post_status($postId) !== false);
154
155 if ($postExists === false) {
156 return; // abort
157 }
158
159 $content = get_the_title($postId) . ' - ' . get_permalink($postId);
160 $mediaUrl = get_the_post_thumbnail_url($postId, 'large');
161 $metricoolCreatePostUrl = MetricoolUrl::createPostUrl($content, $mediaUrl);
162
163 // In Metricool the user can "schedule" the post they share from WP.
164 // That's where the naming mismatch comes from. Share <-> Schedule
165 Event::dispatch(Event::POST_SCHEDULED);
166
167 // Redirect to the deeplink
168 header('Location: ' . $metricoolCreatePostUrl);
169 exit();
170 }
171
172 /**
173 * Adds the Metricool meta box to the post edit screen. Works for both
174 * Gutenberg and classic editor. Also adds the meta box for public
175 * post-types for which the column is not shown by default.
176 */
177 public function addShareMetaBox(): void
178 {
179 foreach ($this->getAllPublicPostTypes() as $postType) {
180 add_meta_box(
181 'metricool-share-post',
182 __('Metricool', 'metricool'),
183 [$this, 'renderShareMetaBox'],
184 $postType,
185 'side',
186 );
187 }
188 }
189
190 /**
191 * Renders the content of the Metricool meta box on the post edit screen.
192 */
193 public function renderShareMetaBox(\WP_Post $post): void
194 {
195 $arguments = [
196 'published' => (strtolower(get_post_status($post->ID)) === 'publish'),
197 'unpublishedText' => __('Sharing is only possible for published posts.', 'metricool'),
198 ];
199
200 $this->render('admin/share-post/meta-box', $this->getArgumentsToSharePost($post->ID, $arguments));
201 }
202
203 /**
204 * Prepares the arguments needed to render the share post button used in:
205 * {@see insertPostTableColumnContent} and the meta box via:
206 * {@see renderShareMetaBox}
207 *
208 * @param ?array $arguments Additional args to merge, overrides defaults
209 */
210 private function getArgumentsToSharePost(int $postID, ?array $arguments = []): array
211 {
212 $actionableUrl = add_query_arg([
213 'metricool_action' => self::SHARE_POST_ACTION,
214 'metricool_post_id' => $postID,
215 '_metricool_action_nonce' => wp_create_nonce('metricool_action'),
216 ], $this->env->getUrl('plugin.dashboard_url'));
217
218 $defaultArguments = [
219 'actionableUrl' => $actionableUrl,
220 'label' => __('Share', 'metricool')
221 ];
222
223 // Merge passed arguments with priority over default arguments
224 return array_merge($defaultArguments, $arguments);
225 }
226 }
227