Passed
Branch master (6aefea)
by litefeel
08:42
created
lib/controller.php 1 patch
Indentation   +228 added lines, -228 removed lines patch added patch discarded remove patch
@@ -9,232 +9,232 @@
 block discarded – undo
9 9
  */
10 10
 class Writing_On_GitHub_Controller {
11 11
 
12
-    /**
13
-     * Application container.
14
-     *
15
-     * @var Writing_On_GitHub
16
-     */
17
-    public $app;
18
-
19
-    /**
20
-     * Instantiates a new Controller object
21
-     *
22
-     * @param Writing_On_GitHub $app Applicatio container.
23
-     */
24
-    public function __construct( Writing_On_GitHub $app ) {
25
-        $this->app = $app;
26
-    }
27
-
28
-    /**
29
-     * Webhook callback as triggered from GitHub push.
30
-     *
31
-     * Reads the Webhook payload and syncs posts as necessary.
32
-     *
33
-     * @return boolean
34
-     */
35
-    public function pull_posts() {
36
-        $this->set_ajax();
37
-        if ( ! $this->app->semaphore()->is_open() ) {
38
-            return $this->app->response()->error( new WP_Error(
39
-                'semaphore_locked',
40
-                sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::pull_posts()' )
41
-            ) );
42
-        }
43
-
44
-        if ( ! $this->app->request()->is_secret_valid() ) {
45
-            return $this->app->response()->error( new WP_Error(
46
-                'invalid_headers',
47
-                __( 'Failed to validate secret.', 'writing-on-github' )
48
-            ) );
49
-        }
50
-
51
-        // ping
52
-        if ( $this->app->request()->is_ping() ) {
53
-            return $this->app->response()->success( __( 'Wordpress is ready.', 'writing-on-github' ) );
54
-        }
55
-
56
-        // push
57
-        if ( ! $this->app->request()->is_push() ) {
58
-            return $this->app->response()->error( new WP_Error(
59
-                'invalid_headers',
60
-                sprintf( 'Failed to validate webhook event: %s.',
61
-                    $this->app->request()->webhook_event() )
62
-            ) );
63
-        }
64
-        $payload = $this->app->request()->payload();
65
-
66
-        $error = $payload->should_import();
67
-        if ( is_wp_error( $error ) ) {
68
-            /* @var WP_Error $error */
69
-            return $this->app->response()->error( $error );
70
-        }
71
-
72
-        $this->app->semaphore()->lock();
73
-        remove_action( 'save_post', array( $this, 'export_post' ) );
74
-        remove_action( 'delete_post', array( $this, 'delete_post' ) );
75
-
76
-        $result = $this->app->import()->payload( $payload );
77
-
78
-        $this->app->semaphore()->unlock();
79
-
80
-        if ( is_wp_error( $result ) ) {
81
-            /* @var WP_Error $result */
82
-            return $this->app->response()->error( $result );
83
-        }
84
-
85
-        return $this->app->response()->success( $result );
86
-    }
87
-
88
-    /**
89
-     * Imports posts from the current master branch.
90
-     * @param  integer $user_id
91
-     * @param  boolean $force
92
-     * @return boolean
93
-     */
94
-    public function import_master( $user_id = 0, $force = false ) {
95
-        if ( ! $this->app->semaphore()->is_open() ) {
96
-            return $this->app->response()->error( new WP_Error(
97
-                'semaphore_locked',
98
-                sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::import_master()' )
99
-            ) );
100
-        }
101
-
102
-        $this->app->semaphore()->lock();
103
-        remove_action( 'save_post', array( $this, 'export_post' ) );
104
-        remove_action( 'save_post', array( $this, 'delete_post' ) );
105
-
106
-        if ( $user_id ) {
107
-            wp_set_current_user( $user_id );
108
-        }
109
-
110
-        $result = $this->app->import()->master( $force );
111
-
112
-        $this->app->semaphore()->unlock();
113
-
114
-        if ( is_wp_error( $result ) ) {
115
-            /* @var WP_Error $result */
116
-            update_option( '_wogh_import_error', $result->get_error_message() );
117
-
118
-            return $this->app->response()->error( $result );
119
-        }
120
-
121
-        update_option( '_wogh_import_complete', 'yes' );
122
-
123
-        return $this->app->response()->success( $result );
124
-    }
125
-
126
-    /**
127
-     * Export all the posts in the database to GitHub.
128
-     *
129
-     * @param  int        $user_id
130
-     * @param  boolean    $force
131
-     * @return boolean
132
-     */
133
-    public function export_all( $user_id = 0, $force = false ) {
134
-        if ( ! $this->app->semaphore()->is_open() ) {
135
-            return $this->app->response()->error( new WP_Error(
136
-                'semaphore_locked',
137
-                sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_all()' )
138
-            ) );
139
-        }
140
-
141
-        $this->app->semaphore()->lock();
142
-
143
-        if ( $user_id ) {
144
-            wp_set_current_user( $user_id );
145
-        }
146
-
147
-        $result = $this->app->export()->full($force);
148
-        $this->app->semaphore()->unlock();
149
-
150
-        // Maybe move option updating out of this class/upgrade message display?
151
-        if ( is_wp_error( $result ) ) {
152
-            /* @var WP_Error $result */
153
-            update_option( '_wogh_export_error', $result->get_error_message() );
154
-
155
-            return $this->app->response()->error( $result );
156
-        } else {
157
-            update_option( '_wogh_export_complete', 'yes' );
158
-            update_option( '_wogh_fully_exported', 'yes' );
159
-
160
-            return $this->app->response()->success( $result );
161
-        }
162
-    }
163
-
164
-    /**
165
-     * Exports a single post to GitHub by ID.
166
-     *
167
-     * Called on the save_post hook.
168
-     *
169
-     * @param int $post_id Post ID.
170
-     *
171
-     * @return boolean
172
-     */
173
-    public function export_post( $post_id ) {
174
-        if ( wp_is_post_revision( $post_id ) ) {
175
-            return;
176
-        }
177
-
178
-        if ( ! $this->app->semaphore()->is_open() ) {
179
-            return $this->app->response()->error( new WP_Error(
180
-                'semaphore_locked',
181
-                sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_post()' )
182
-            ) );
183
-        }
184
-
185
-        $this->app->semaphore()->lock();
186
-        $result = $this->app->export()->update( $post_id );
187
-        $this->app->semaphore()->unlock();
188
-
189
-        if ( is_wp_error( $result ) ) {
190
-            /* @var WP_Error $result */
191
-            return $this->app->response()->error( $result );
192
-        }
193
-
194
-        return $this->app->response()->success( $result );
195
-    }
196
-
197
-    /**
198
-     * Removes the post from the tree.
199
-     *
200
-     * Called the delete_post hook.
201
-     *
202
-     * @param int $post_id Post ID.
203
-     *
204
-     * @return boolean
205
-     */
206
-    public function delete_post( $post_id ) {
207
-        if ( wp_is_post_revision( $post_id ) ) {
208
-            return;
209
-        }
210
-
211
-        if ( ! $this->app->semaphore()->is_open() ) {
212
-            return $this->app->response()->error( new WP_Error(
213
-                'semaphore_locked',
214
-                sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::delete_post()' )
215
-            ) );
216
-        }
217
-
218
-        $this->app->semaphore()->lock();
219
-        $result = $this->app->export()->delete( $post_id );
220
-        $this->app->semaphore()->unlock();
221
-
222
-        if ( is_wp_error( $result ) ) {
223
-            /* @var WP_Error $result */
224
-            return $this->app->response()->error( $result );
225
-        }
226
-
227
-        return $this->app->response()->success( $result );
228
-    }
229
-
230
-    /**
231
-     * Indicates we're running our own AJAX hook
232
-     * and thus should respond with JSON, rather
233
-     * than just returning data.
234
-     */
235
-    protected function set_ajax() {
236
-        if ( ! defined( 'WOGH_AJAX' ) ) {
237
-            define( 'WOGH_AJAX', true );
238
-        }
239
-    }
12
+	/**
13
+	 * Application container.
14
+	 *
15
+	 * @var Writing_On_GitHub
16
+	 */
17
+	public $app;
18
+
19
+	/**
20
+	 * Instantiates a new Controller object
21
+	 *
22
+	 * @param Writing_On_GitHub $app Applicatio container.
23
+	 */
24
+	public function __construct( Writing_On_GitHub $app ) {
25
+		$this->app = $app;
26
+	}
27
+
28
+	/**
29
+	 * Webhook callback as triggered from GitHub push.
30
+	 *
31
+	 * Reads the Webhook payload and syncs posts as necessary.
32
+	 *
33
+	 * @return boolean
34
+	 */
35
+	public function pull_posts() {
36
+		$this->set_ajax();
37
+		if ( ! $this->app->semaphore()->is_open() ) {
38
+			return $this->app->response()->error( new WP_Error(
39
+				'semaphore_locked',
40
+				sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::pull_posts()' )
41
+			) );
42
+		}
43
+
44
+		if ( ! $this->app->request()->is_secret_valid() ) {
45
+			return $this->app->response()->error( new WP_Error(
46
+				'invalid_headers',
47
+				__( 'Failed to validate secret.', 'writing-on-github' )
48
+			) );
49
+		}
50
+
51
+		// ping
52
+		if ( $this->app->request()->is_ping() ) {
53
+			return $this->app->response()->success( __( 'Wordpress is ready.', 'writing-on-github' ) );
54
+		}
55
+
56
+		// push
57
+		if ( ! $this->app->request()->is_push() ) {
58
+			return $this->app->response()->error( new WP_Error(
59
+				'invalid_headers',
60
+				sprintf( 'Failed to validate webhook event: %s.',
61
+					$this->app->request()->webhook_event() )
62
+			) );
63
+		}
64
+		$payload = $this->app->request()->payload();
65
+
66
+		$error = $payload->should_import();
67
+		if ( is_wp_error( $error ) ) {
68
+			/* @var WP_Error $error */
69
+			return $this->app->response()->error( $error );
70
+		}
71
+
72
+		$this->app->semaphore()->lock();
73
+		remove_action( 'save_post', array( $this, 'export_post' ) );
74
+		remove_action( 'delete_post', array( $this, 'delete_post' ) );
75
+
76
+		$result = $this->app->import()->payload( $payload );
77
+
78
+		$this->app->semaphore()->unlock();
79
+
80
+		if ( is_wp_error( $result ) ) {
81
+			/* @var WP_Error $result */
82
+			return $this->app->response()->error( $result );
83
+		}
84
+
85
+		return $this->app->response()->success( $result );
86
+	}
87
+
88
+	/**
89
+	 * Imports posts from the current master branch.
90
+	 * @param  integer $user_id
91
+	 * @param  boolean $force
92
+	 * @return boolean
93
+	 */
94
+	public function import_master( $user_id = 0, $force = false ) {
95
+		if ( ! $this->app->semaphore()->is_open() ) {
96
+			return $this->app->response()->error( new WP_Error(
97
+				'semaphore_locked',
98
+				sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::import_master()' )
99
+			) );
100
+		}
101
+
102
+		$this->app->semaphore()->lock();
103
+		remove_action( 'save_post', array( $this, 'export_post' ) );
104
+		remove_action( 'save_post', array( $this, 'delete_post' ) );
105
+
106
+		if ( $user_id ) {
107
+			wp_set_current_user( $user_id );
108
+		}
109
+
110
+		$result = $this->app->import()->master( $force );
111
+
112
+		$this->app->semaphore()->unlock();
113
+
114
+		if ( is_wp_error( $result ) ) {
115
+			/* @var WP_Error $result */
116
+			update_option( '_wogh_import_error', $result->get_error_message() );
117
+
118
+			return $this->app->response()->error( $result );
119
+		}
120
+
121
+		update_option( '_wogh_import_complete', 'yes' );
122
+
123
+		return $this->app->response()->success( $result );
124
+	}
125
+
126
+	/**
127
+	 * Export all the posts in the database to GitHub.
128
+	 *
129
+	 * @param  int        $user_id
130
+	 * @param  boolean    $force
131
+	 * @return boolean
132
+	 */
133
+	public function export_all( $user_id = 0, $force = false ) {
134
+		if ( ! $this->app->semaphore()->is_open() ) {
135
+			return $this->app->response()->error( new WP_Error(
136
+				'semaphore_locked',
137
+				sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_all()' )
138
+			) );
139
+		}
140
+
141
+		$this->app->semaphore()->lock();
142
+
143
+		if ( $user_id ) {
144
+			wp_set_current_user( $user_id );
145
+		}
146
+
147
+		$result = $this->app->export()->full($force);
148
+		$this->app->semaphore()->unlock();
149
+
150
+		// Maybe move option updating out of this class/upgrade message display?
151
+		if ( is_wp_error( $result ) ) {
152
+			/* @var WP_Error $result */
153
+			update_option( '_wogh_export_error', $result->get_error_message() );
154
+
155
+			return $this->app->response()->error( $result );
156
+		} else {
157
+			update_option( '_wogh_export_complete', 'yes' );
158
+			update_option( '_wogh_fully_exported', 'yes' );
159
+
160
+			return $this->app->response()->success( $result );
161
+		}
162
+	}
163
+
164
+	/**
165
+	 * Exports a single post to GitHub by ID.
166
+	 *
167
+	 * Called on the save_post hook.
168
+	 *
169
+	 * @param int $post_id Post ID.
170
+	 *
171
+	 * @return boolean
172
+	 */
173
+	public function export_post( $post_id ) {
174
+		if ( wp_is_post_revision( $post_id ) ) {
175
+			return;
176
+		}
177
+
178
+		if ( ! $this->app->semaphore()->is_open() ) {
179
+			return $this->app->response()->error( new WP_Error(
180
+				'semaphore_locked',
181
+				sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_post()' )
182
+			) );
183
+		}
184
+
185
+		$this->app->semaphore()->lock();
186
+		$result = $this->app->export()->update( $post_id );
187
+		$this->app->semaphore()->unlock();
188
+
189
+		if ( is_wp_error( $result ) ) {
190
+			/* @var WP_Error $result */
191
+			return $this->app->response()->error( $result );
192
+		}
193
+
194
+		return $this->app->response()->success( $result );
195
+	}
196
+
197
+	/**
198
+	 * Removes the post from the tree.
199
+	 *
200
+	 * Called the delete_post hook.
201
+	 *
202
+	 * @param int $post_id Post ID.
203
+	 *
204
+	 * @return boolean
205
+	 */
206
+	public function delete_post( $post_id ) {
207
+		if ( wp_is_post_revision( $post_id ) ) {
208
+			return;
209
+		}
210
+
211
+		if ( ! $this->app->semaphore()->is_open() ) {
212
+			return $this->app->response()->error( new WP_Error(
213
+				'semaphore_locked',
214
+				sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::delete_post()' )
215
+			) );
216
+		}
217
+
218
+		$this->app->semaphore()->lock();
219
+		$result = $this->app->export()->delete( $post_id );
220
+		$this->app->semaphore()->unlock();
221
+
222
+		if ( is_wp_error( $result ) ) {
223
+			/* @var WP_Error $result */
224
+			return $this->app->response()->error( $result );
225
+		}
226
+
227
+		return $this->app->response()->success( $result );
228
+	}
229
+
230
+	/**
231
+	 * Indicates we're running our own AJAX hook
232
+	 * and thus should respond with JSON, rather
233
+	 * than just returning data.
234
+	 */
235
+	protected function set_ajax() {
236
+		if ( ! defined( 'WOGH_AJAX' ) ) {
237
+			define( 'WOGH_AJAX', true );
238
+		}
239
+	}
240 240
 }
Please login to merge, or discard this patch.
lib/request.php 1 patch
Indentation   +135 added lines, -135 removed lines patch added patch discarded remove patch
@@ -9,139 +9,139 @@
 block discarded – undo
9 9
  */
10 10
 class Writing_On_GitHub_Request {
11 11
 
12
-    /**
13
-     * Application container.
14
-     *
15
-     * @var Writing_On_GitHub
16
-     */
17
-    protected $app;
18
-
19
-    /**
20
-     * Raw request data.
21
-     *
22
-     * @var string
23
-     */
24
-    protected $raw_data;
25
-
26
-    /**
27
-     * Headers
28
-     * @var array
29
-     */
30
-    protected $headers;
31
-
32
-    /**
33
-     * Writing_On_GitHub_Request constructor.
34
-     *
35
-     * @param Writing_On_GitHub $app Application container.
36
-     */
37
-    public function __construct( Writing_On_GitHub $app ) {
38
-        $this->app = $app;
39
-    }
40
-
41
-    /**
42
-     * Validates the header's secret.
43
-     *
44
-     * @return true|WP_Error
45
-     */
46
-    public function is_secret_valid() {
47
-        $headers = $this->headers();
48
-
49
-        $this->raw_data = $this->read_raw_data();
50
-
51
-        // Validate request secret.
52
-        $hash = hash_hmac( 'sha1', $this->raw_data, $this->secret() );
53
-        if ( 'sha1=' . $hash !== $headers['X-Hub-Signature'] ) {
54
-            return false;
55
-        }
56
-
57
-        //      [X-Hub-Signature] => sha1=3cf3da70de401f7dfff053392f60cc534efed3b4
58
-        //     [Content-Type] => application/json
59
-        //     [X-Github-Delivery] => b2102500-0acf-11e7-8acb-fd86a3497c2f
60
-        //     [X-Github-Event] => ping
61
-
62
-        return true;
63
-    }
64
-
65
-    /**
66
-     * Validates the ping event.
67
-     * @return boolean
68
-     */
69
-    public function is_ping() {
70
-        return 'ping' == $this->webhook_event();
71
-    }
72
-
73
-    /**
74
-     * Validates the push event.
75
-     * @return boolean
76
-     */
77
-    public function is_push() {
78
-        return 'push' == $this->webhook_event();
79
-    }
80
-
81
-    /**
82
-     * Return X-Github-Event in headers.
83
-     * @return string
84
-     */
85
-    public function webhook_event() {
86
-        $headers = $this->headers();
87
-        return $headers['X-Github-Event'];
88
-    }
89
-
90
-    /**
91
-     * Returns a payload object for the given request.
92
-     *
93
-     * @return Writing_On_GitHub_Payload
94
-     */
95
-    public function payload() {
96
-        return new Writing_On_GitHub_Payload( $this->app, $this->raw_data );
97
-    }
98
-
99
-    /**
100
-     * Cross-server header support.
101
-     *
102
-     * Returns an array of the request's headers.
103
-     *
104
-     * @return array
105
-     */
106
-    protected function headers() {
107
-        if ( ! empty( $this->headers ) ) {
108
-            return $this->headers;
109
-        }
110
-
111
-        if ( function_exists( 'getallheaders' ) ) {
112
-
113
-            $this->headers = getallheaders();
114
-            return $this->headers;
115
-        }
116
-        /**
117
-         * Nginx and pre 5.4 workaround.
118
-         * @see http://www.php.net/manual/en/function.getallheaders.php
119
-         */
120
-        $this->headers = array();
121
-        foreach ( $_SERVER as $name => $value ) {
122
-            if ( 'HTTP_' === substr( $name, 0, 5 ) ) {
123
-                $this->headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value;
124
-            }
125
-        }
126
-
127
-        return $this->headers;
128
-    }
129
-
130
-    /**
131
-     * Reads the raw data from STDIN.
132
-     *
133
-     * @return string
134
-     */
135
-    protected function read_raw_data() {
136
-        return file_get_contents( 'php://input' );
137
-    }
138
-
139
-    /**
140
-     * Returns the Webhook secret
141
-     *
142
-     * @return string
143
-     */
144
-    protected function secret() {
145
-        return get_option( 'wogh_secret' );
146
-    }
12
+	/**
13
+	 * Application container.
14
+	 *
15
+	 * @var Writing_On_GitHub
16
+	 */
17
+	protected $app;
18
+
19
+	/**
20
+	 * Raw request data.
21
+	 *
22
+	 * @var string
23
+	 */
24
+	protected $raw_data;
25
+
26
+	/**
27
+	 * Headers
28
+	 * @var array
29
+	 */
30
+	protected $headers;
31
+
32
+	/**
33
+	 * Writing_On_GitHub_Request constructor.
34
+	 *
35
+	 * @param Writing_On_GitHub $app Application container.
36
+	 */
37
+	public function __construct( Writing_On_GitHub $app ) {
38
+		$this->app = $app;
39
+	}
40
+
41
+	/**
42
+	 * Validates the header's secret.
43
+	 *
44
+	 * @return true|WP_Error
45
+	 */
46
+	public function is_secret_valid() {
47
+		$headers = $this->headers();
48
+
49
+		$this->raw_data = $this->read_raw_data();
50
+
51
+		// Validate request secret.
52
+		$hash = hash_hmac( 'sha1', $this->raw_data, $this->secret() );
53
+		if ( 'sha1=' . $hash !== $headers['X-Hub-Signature'] ) {
54
+			return false;
55
+		}
56
+
57
+		//      [X-Hub-Signature] => sha1=3cf3da70de401f7dfff053392f60cc534efed3b4
58
+		//     [Content-Type] => application/json
59
+		//     [X-Github-Delivery] => b2102500-0acf-11e7-8acb-fd86a3497c2f
60
+		//     [X-Github-Event] => ping
61
+
62
+		return true;
63
+	}
64
+
65
+	/**
66
+	 * Validates the ping event.
67
+	 * @return boolean
68
+	 */
69
+	public function is_ping() {
70
+		return 'ping' == $this->webhook_event();
71
+	}
72
+
73
+	/**
74
+	 * Validates the push event.
75
+	 * @return boolean
76
+	 */
77
+	public function is_push() {
78
+		return 'push' == $this->webhook_event();
79
+	}
80
+
81
+	/**
82
+	 * Return X-Github-Event in headers.
83
+	 * @return string
84
+	 */
85
+	public function webhook_event() {
86
+		$headers = $this->headers();
87
+		return $headers['X-Github-Event'];
88
+	}
89
+
90
+	/**
91
+	 * Returns a payload object for the given request.
92
+	 *
93
+	 * @return Writing_On_GitHub_Payload
94
+	 */
95
+	public function payload() {
96
+		return new Writing_On_GitHub_Payload( $this->app, $this->raw_data );
97
+	}
98
+
99
+	/**
100
+	 * Cross-server header support.
101
+	 *
102
+	 * Returns an array of the request's headers.
103
+	 *
104
+	 * @return array
105
+	 */
106
+	protected function headers() {
107
+		if ( ! empty( $this->headers ) ) {
108
+			return $this->headers;
109
+		}
110
+
111
+		if ( function_exists( 'getallheaders' ) ) {
112
+
113
+			$this->headers = getallheaders();
114
+			return $this->headers;
115
+		}
116
+		/**
117
+		 * Nginx and pre 5.4 workaround.
118
+		 * @see http://www.php.net/manual/en/function.getallheaders.php
119
+		 */
120
+		$this->headers = array();
121
+		foreach ( $_SERVER as $name => $value ) {
122
+			if ( 'HTTP_' === substr( $name, 0, 5 ) ) {
123
+				$this->headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value;
124
+			}
125
+		}
126
+
127
+		return $this->headers;
128
+	}
129
+
130
+	/**
131
+	 * Reads the raw data from STDIN.
132
+	 *
133
+	 * @return string
134
+	 */
135
+	protected function read_raw_data() {
136
+		return file_get_contents( 'php://input' );
137
+	}
138
+
139
+	/**
140
+	 * Returns the Webhook secret
141
+	 *
142
+	 * @return string
143
+	 */
144
+	protected function secret() {
145
+		return get_option( 'wogh_secret' );
146
+	}
147 147
 }
Please login to merge, or discard this patch.