Version Description
- store connections using a taxonomy instead of postmeta
- more info
Download this release
Release Info
Developer | scribu |
Plugin | Posts 2 Posts |
Version | 0.3 |
Comparing to | |
See all releases |
Code changes from version 0.2 to 0.3
- admin/admin.php +73 -24
- api.php +69 -119
- core.php +77 -0
- lang/posts-to-posts.pot +8 -8
- posts-to-posts.php +5 -4
- readme.txt +14 -7
- scb/Query.php +0 -75
- scb/QueryManipulation.php +81 -0
- scb/Util.php +10 -2
- scb/load.php +3 -2
admin/admin.php
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
class
|
|
|
|
|
4 |
|
5 |
function init( $file ) {
|
6 |
add_action( 'admin_print_styles-post.php', array( __CLASS__, 'scripts' ) );
|
@@ -8,10 +10,34 @@ class P2P_Box {
|
|
8 |
|
9 |
add_action( 'add_meta_boxes', array( __CLASS__, 'register' ) );
|
10 |
|
11 |
-
add_action( 'save_post', array( __CLASS__, 'save' ), 10
|
12 |
add_action( 'wp_ajax_p2p_search', array( __CLASS__, 'ajax_search' ) );
|
13 |
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
}
|
16 |
|
17 |
function scripts() {
|
@@ -26,28 +52,42 @@ class P2P_Box {
|
|
26 |
<?php
|
27 |
}
|
28 |
|
29 |
-
function save( $post_a
|
30 |
-
|
|
|
31 |
return;
|
32 |
|
33 |
-
|
34 |
|
35 |
-
foreach ( p2p_get_connection_types( $
|
36 |
if ( !isset( $_POST['p2p_connected_ids_' . $post_type] ) )
|
37 |
continue;
|
38 |
|
39 |
-
$
|
40 |
-
$new_connections = explode( ',', $_POST[ 'p2p_connected_ids_' . $post_type ] );
|
41 |
|
42 |
-
|
43 |
-
|
44 |
|
45 |
-
|
46 |
-
|
47 |
}
|
48 |
}
|
49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
function register( $post_type ) {
|
|
|
|
|
|
|
|
|
51 |
foreach ( p2p_get_connection_types( $post_type ) as $type ) {
|
52 |
add_meta_box(
|
53 |
'p2p-connections-' . $type,
|
@@ -63,7 +103,9 @@ class P2P_Box {
|
|
63 |
|
64 |
function box( $post, $args ) {
|
65 |
$post_type = $args['args'];
|
66 |
-
|
|
|
|
|
67 |
?>
|
68 |
|
69 |
<div class="p2p_metabox">
|
@@ -114,11 +156,24 @@ class P2P_Box {
|
|
114 |
}
|
115 |
|
116 |
function ajax_search() {
|
117 |
-
$
|
118 |
-
|
|
|
|
|
|
|
|
|
119 |
's' => $_GET['q'],
|
120 |
-
'post_type' => $
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
|
123 |
$results = array();
|
124 |
while ( $posts->have_posts() ) {
|
@@ -139,11 +194,5 @@ class P2P_Box {
|
|
139 |
|
140 |
return scbUtil::objects_to_assoc( get_posts( $args ), 'ID', 'post_title' );
|
141 |
}
|
142 |
-
|
143 |
-
function uninstall() {
|
144 |
-
global $wpdb;
|
145 |
-
|
146 |
-
$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '" . P2P_META_KEY . "'" );
|
147 |
-
}
|
148 |
}
|
149 |
|
1 |
<?php
|
2 |
|
3 |
+
class P2P_Admin {
|
4 |
+
|
5 |
+
private static $connections;
|
6 |
|
7 |
function init( $file ) {
|
8 |
add_action( 'admin_print_styles-post.php', array( __CLASS__, 'scripts' ) );
|
10 |
|
11 |
add_action( 'add_meta_boxes', array( __CLASS__, 'register' ) );
|
12 |
|
13 |
+
add_action( 'save_post', array( __CLASS__, 'save' ), 10 );
|
14 |
add_action( 'wp_ajax_p2p_search', array( __CLASS__, 'ajax_search' ) );
|
15 |
|
16 |
+
add_action( 'admin_notices', array( __CLASS__, 'migrate' ) );
|
17 |
+
}
|
18 |
+
|
19 |
+
function migrate() {
|
20 |
+
if ( !isset( $_GET['migrate_p2p'] ) || !current_user_can( 'administrator' ) )
|
21 |
+
return;
|
22 |
+
|
23 |
+
global $wpdb;
|
24 |
+
|
25 |
+
$rows = $wpdb->get_results( "
|
26 |
+
SELECT post_id as post_a, meta_value as post_b
|
27 |
+
FROM $wpdb->postmeta
|
28 |
+
WHERE meta_key = '_p2p'
|
29 |
+
" );
|
30 |
+
|
31 |
+
$grouped = array();
|
32 |
+
foreach ( $rows as $row )
|
33 |
+
$grouped[ $row->post_a ][] = $row->post_b;
|
34 |
+
|
35 |
+
foreach ( $grouped as $post_a => $post_b )
|
36 |
+
p2p_connect( $post_a, $post_b );
|
37 |
+
|
38 |
+
$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_p2p'" );
|
39 |
+
|
40 |
+
printf( "<div class='updated'><p>Migrated %s connections.</p></div>", count( $rows ) );
|
41 |
}
|
42 |
|
43 |
function scripts() {
|
52 |
<?php
|
53 |
}
|
54 |
|
55 |
+
function save( $post_a ) {
|
56 |
+
$current_ptype = get_post_type( $post_a );
|
57 |
+
if ( defined( 'DOING_AJAX' ) || defined( 'DOING_CRON' ) || empty( $_POST ) || 'revision' == $current_ptype )
|
58 |
return;
|
59 |
|
60 |
+
self::cache_connections( $post_a );
|
61 |
|
62 |
+
foreach ( p2p_get_connection_types( $current_ptype ) as $post_type ) {
|
63 |
if ( !isset( $_POST['p2p_connected_ids_' . $post_type] ) )
|
64 |
continue;
|
65 |
|
66 |
+
$reciprocal = p2p_connection_type_is_reciprocal( $current_ptype, $post_type );
|
|
|
67 |
|
68 |
+
$old_connections = (array) @self::$connections[ $post_type ];
|
69 |
+
$new_connections = explode( ',', $_POST[ 'p2p_connected_ids_' . $post_type ] );
|
70 |
|
71 |
+
p2p_disconnect( $post_a, array_diff( $old_connections, $new_connections ), $reciprocal );
|
72 |
+
p2p_connect( $post_a, array_diff( $new_connections, $old_connections ), $reciprocal );
|
73 |
}
|
74 |
}
|
75 |
|
76 |
+
private function cache_connections( $post_id ) {
|
77 |
+
$posts = p2p_get_connected( $post_id, 'from', 'any', 'objects' );
|
78 |
+
|
79 |
+
$connections = array();
|
80 |
+
foreach ( $posts as $post )
|
81 |
+
$connections[ $post->post_type ][] = $post->ID;
|
82 |
+
|
83 |
+
self::$connections = $connections;
|
84 |
+
}
|
85 |
+
|
86 |
function register( $post_type ) {
|
87 |
+
global $post;
|
88 |
+
|
89 |
+
self::cache_connections( $post->ID );
|
90 |
+
|
91 |
foreach ( p2p_get_connection_types( $post_type ) as $type ) {
|
92 |
add_meta_box(
|
93 |
'p2p-connections-' . $type,
|
103 |
|
104 |
function box( $post, $args ) {
|
105 |
$post_type = $args['args'];
|
106 |
+
|
107 |
+
$connected_ids = @self::$connections[ $post_type ];
|
108 |
+
|
109 |
?>
|
110 |
|
111 |
<div class="p2p_metabox">
|
156 |
}
|
157 |
|
158 |
function ajax_search() {
|
159 |
+
$post_type_name = $_GET['post_type'];
|
160 |
+
|
161 |
+
if ( !post_type_exists( $post_type_name ) )
|
162 |
+
die;
|
163 |
+
|
164 |
+
$args = array(
|
165 |
's' => $_GET['q'],
|
166 |
+
'post_type' => $post_type_name,
|
167 |
+
'post_status' => 'any',
|
168 |
+
'posts_per_page' => 5,
|
169 |
+
'order' => 'ASC',
|
170 |
+
'orderby' => 'title',
|
171 |
+
'suppress_filters' => true,
|
172 |
+
'update_post_term_cache' => false,
|
173 |
+
'update_post_meta_cache' => false
|
174 |
+
);
|
175 |
+
|
176 |
+
$posts = new WP_Query( $args );
|
177 |
|
178 |
$results = array();
|
179 |
while ( $posts->have_posts() ) {
|
194 |
|
195 |
return scbUtil::objects_to_assoc( get_posts( $args ), 'ID', 'post_title' );
|
196 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
}
|
198 |
|
api.php
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
-
* Register a connection between two post types.
|
5 |
* This creates the appropriate meta box in the admin edit screen
|
6 |
*
|
7 |
* @param string $post_type_a The first end of the connection
|
8 |
-
* @param string $
|
9 |
-
* @param bool $
|
10 |
*/
|
11 |
-
function p2p_register_connection_type( $post_type_a, $post_type_b, $
|
12 |
if ( !$ptype = get_post_type_object( $post_type_a ) )
|
13 |
return;
|
14 |
|
@@ -22,7 +22,7 @@ function p2p_register_connection_type( $post_type_a, $post_type_b, $bydirectiona
|
|
22 |
|
23 |
$ptype->can_connect_to = array_merge( $ptype->can_connect_to, $post_type_b );
|
24 |
|
25 |
-
if ( $
|
26 |
foreach ( $post_type_b as $ptype_b )
|
27 |
p2p_register_connection_type( $ptype_b, $post_type_a, false );
|
28 |
}
|
@@ -38,32 +38,48 @@ function p2p_get_connection_types( $post_type_a ) {
|
|
38 |
return (array) @get_post_type_object( $post_type_a )->can_connect_to;
|
39 |
}
|
40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
/**
|
42 |
* Connect a post to another one
|
43 |
*
|
44 |
* @param int $post_a The first end of the connection
|
45 |
-
* @param int $post_b The second end of the connection
|
46 |
-
* @param bool $
|
47 |
*/
|
48 |
-
function p2p_connect( $post_a, $post_b, $
|
49 |
-
|
50 |
|
51 |
-
if ( $
|
52 |
-
|
|
|
53 |
}
|
54 |
|
55 |
/**
|
56 |
* Disconnect a post from another one
|
57 |
*
|
58 |
* @param int $post_a The first end of the connection
|
59 |
-
* @param int $post_b The second end of the connection
|
60 |
-
* @param bool $
|
61 |
*/
|
62 |
-
function p2p_disconnect( $post_a, $post_b, $
|
63 |
-
|
64 |
|
65 |
-
if ( $
|
66 |
-
|
|
|
67 |
}
|
68 |
|
69 |
/**
|
@@ -71,15 +87,14 @@ function p2p_disconnect( $post_a, $post_b, $bydirectional = false ) {
|
|
71 |
*
|
72 |
* @param int $post_a The first end of the connection
|
73 |
* @param int $post_b The second end of the connection
|
74 |
-
* @param bool $bydirectional Wether the connection should be bydirectional
|
75 |
*
|
76 |
* @return bool True if the connection exists, false otherwise
|
77 |
*/
|
78 |
-
function p2p_is_connected( $post_a, $post_b, $
|
79 |
-
$r = (
|
80 |
|
81 |
-
if ( $
|
82 |
-
$r = $r &&
|
83 |
|
84 |
return $r;
|
85 |
}
|
@@ -87,120 +102,55 @@ function p2p_is_connected( $post_a, $post_b, $bydirectional = false ) {
|
|
87 |
/**
|
88 |
* Get the list of connected posts
|
89 |
*
|
90 |
-
* @param string $post_type The post type of the connected posts.
|
91 |
-
* @param string $direction The direction of the connection. Can be 'to' or 'from'
|
92 |
* @param int $post_id One end of the connection
|
93 |
-
* @param bool $grouped Wether the results should be grouped by post type
|
94 |
-
*
|
95 |
-
* @return array[int] if $grouped is True
|
96 |
-
* @return array[string => array[int]] if $grouped is False
|
97 |
-
*/
|
98 |
-
function p2p_get_connected( $post_type, $direction, $post_id, $grouped = false ) {
|
99 |
-
global $wpdb;
|
100 |
-
|
101 |
-
$post_id = absint( $post_id );
|
102 |
-
|
103 |
-
if ( !$post_id || ( 'any' != $post_type && !post_type_exists( $post_type ) ) )
|
104 |
-
return false;
|
105 |
-
|
106 |
-
if ( 'to' == $direction ) {
|
107 |
-
$col_a = 'post_id';
|
108 |
-
$col_b = 'meta_value';
|
109 |
-
} else {
|
110 |
-
$col_b = 'post_id';
|
111 |
-
$col_a = 'meta_value';
|
112 |
-
}
|
113 |
-
|
114 |
-
if ( 'any' == $post_type && $grouped ) {
|
115 |
-
$query = "
|
116 |
-
SELECT $col_a AS post_id, (
|
117 |
-
SELECT post_type
|
118 |
-
FROM $wpdb->posts
|
119 |
-
WHERE $wpdb->posts.ID = $col_a
|
120 |
-
) AS type
|
121 |
-
FROM $wpdb->postmeta
|
122 |
-
WHERE meta_key = '" . P2P_META_KEY . "'
|
123 |
-
AND $col_b = $post_id
|
124 |
-
";
|
125 |
-
|
126 |
-
$connections = array();
|
127 |
-
foreach ( $wpdb->get_results( $query ) as $row )
|
128 |
-
$connections[$row->type][] = $row->post_id;
|
129 |
-
|
130 |
-
return $connections;
|
131 |
-
}
|
132 |
-
|
133 |
-
$where = "
|
134 |
-
WHERE meta_key = '" . P2P_META_KEY . "'
|
135 |
-
AND $col_b = $post_id
|
136 |
-
";
|
137 |
-
|
138 |
-
if ( 'any' != $post_type )
|
139 |
-
$where .= $wpdb->prepare( "
|
140 |
-
AND $col_a IN (
|
141 |
-
SELECT ID
|
142 |
-
FROM $wpdb->posts
|
143 |
-
WHERE post_type = %s
|
144 |
-
)
|
145 |
-
", $post_type );
|
146 |
-
|
147 |
-
$connections = $wpdb->get_col( "
|
148 |
-
SELECT $col_a
|
149 |
-
FROM $wpdb->postmeta
|
150 |
-
$where
|
151 |
-
" );
|
152 |
-
|
153 |
-
if ( $grouped )
|
154 |
-
return array( $post_type => $connections );
|
155 |
-
|
156 |
-
return $connections;
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* Display the list of connected posts
|
161 |
-
*
|
162 |
-
* @param string $post_type The post type of the connected posts.
|
163 |
* @param string $direction The direction of the connection. Can be 'to' or 'from'
|
164 |
-
* @param
|
165 |
-
* @param
|
|
|
|
|
|
|
166 |
*/
|
167 |
-
function
|
168 |
-
|
169 |
-
$post_id = get_the_ID();
|
170 |
|
171 |
-
|
|
|
172 |
|
173 |
-
if (
|
174 |
-
return;
|
175 |
|
176 |
$args = array(
|
177 |
-
'post__in' => $
|
178 |
'post_type'=> $post_type,
|
|
|
179 |
'nopaging' => true,
|
180 |
);
|
181 |
-
$query = new WP_Query( $args );
|
182 |
|
183 |
-
|
184 |
-
|
|
|
|
|
185 |
|
186 |
-
|
|
|
187 |
|
188 |
-
|
189 |
}
|
190 |
|
191 |
/**
|
192 |
-
*
|
193 |
-
* Lists the posts as an unordered list
|
194 |
*
|
195 |
-
* @param
|
196 |
*/
|
197 |
-
function
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
|
|
|
|
205 |
}
|
206 |
|
1 |
<?php
|
2 |
|
3 |
/**
|
4 |
+
* Register a connection between two post types.
|
5 |
* This creates the appropriate meta box in the admin edit screen
|
6 |
*
|
7 |
* @param string $post_type_a The first end of the connection
|
8 |
+
* @param string|array $post_type_b The second end of the connection
|
9 |
+
* @param bool $reciprocal Wether the connection should be reciprocal
|
10 |
*/
|
11 |
+
function p2p_register_connection_type( $post_type_a, $post_type_b, $reciprocal = false ) {
|
12 |
if ( !$ptype = get_post_type_object( $post_type_a ) )
|
13 |
return;
|
14 |
|
22 |
|
23 |
$ptype->can_connect_to = array_merge( $ptype->can_connect_to, $post_type_b );
|
24 |
|
25 |
+
if ( $reciprocal )
|
26 |
foreach ( $post_type_b as $ptype_b )
|
27 |
p2p_register_connection_type( $ptype_b, $post_type_a, false );
|
28 |
}
|
38 |
return (array) @get_post_type_object( $post_type_a )->can_connect_to;
|
39 |
}
|
40 |
|
41 |
+
/**
|
42 |
+
* Check wether a connection type is reciprocal
|
43 |
+
*
|
44 |
+
* @param string $post_type_a The first end of the connection
|
45 |
+
* @param string $post_type_b The second end of the connection
|
46 |
+
*
|
47 |
+
* @return bool
|
48 |
+
*/
|
49 |
+
function p2p_connection_type_is_reciprocal( $post_type_a, $post_type_b ) {
|
50 |
+
return
|
51 |
+
in_array( $post_type_b, p2p_get_connection_types( $post_type_a ) ) &&
|
52 |
+
in_array( $post_type_a, p2p_get_connection_types( $post_type_b ) );
|
53 |
+
}
|
54 |
+
|
55 |
/**
|
56 |
* Connect a post to another one
|
57 |
*
|
58 |
* @param int $post_a The first end of the connection
|
59 |
+
* @param int|array $post_b The second end of the connection
|
60 |
+
* @param bool $reciprocal Wether the connection is reciprocal or not
|
61 |
*/
|
62 |
+
function p2p_connect( $post_a, $post_b, $reciprocal = false ) {
|
63 |
+
Posts2Posts::connect( $post_a, $post_b );
|
64 |
|
65 |
+
if ( $reciprocal )
|
66 |
+
foreach ( $post_b as $single )
|
67 |
+
Posts2Posts::connect( $single, $post_a );
|
68 |
}
|
69 |
|
70 |
/**
|
71 |
* Disconnect a post from another one
|
72 |
*
|
73 |
* @param int $post_a The first end of the connection
|
74 |
+
* @param int|array $post_b The second end of the connection
|
75 |
+
* @param bool $reciprocal Wether the connection is reciprocal or not
|
76 |
*/
|
77 |
+
function p2p_disconnect( $post_a, $post_b, $reciprocal = false ) {
|
78 |
+
Posts2Posts::disconnect( $post_a, $post_b );
|
79 |
|
80 |
+
if ( $reciprocal )
|
81 |
+
foreach ( $post_b as $single )
|
82 |
+
Posts2Posts::disconnect( $single, $post_a );
|
83 |
}
|
84 |
|
85 |
/**
|
87 |
*
|
88 |
* @param int $post_a The first end of the connection
|
89 |
* @param int $post_b The second end of the connection
|
|
|
90 |
*
|
91 |
* @return bool True if the connection exists, false otherwise
|
92 |
*/
|
93 |
+
function p2p_is_connected( $post_a, $post_b, $reciprocal = false ) {
|
94 |
+
$r = Posts2Posts::is_connected( $post_a, $post_b );
|
95 |
|
96 |
+
if ( $reciprocal )
|
97 |
+
$r = $r && Posts2Posts::is_connected( $post_b, $post_a );
|
98 |
|
99 |
return $r;
|
100 |
}
|
102 |
/**
|
103 |
* Get the list of connected posts
|
104 |
*
|
|
|
|
|
105 |
* @param int $post_id One end of the connection
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
* @param string $direction The direction of the connection. Can be 'to' or 'from'
|
107 |
+
* @param string|array $post_type The post type of the connected posts.
|
108 |
+
* @param string $output Can be 'ids' or 'objects'
|
109 |
+
*
|
110 |
+
* @return array A list of post_ids if $output = 'ids'
|
111 |
+
* @return object A WP_Query instance otherwise
|
112 |
*/
|
113 |
+
function p2p_get_connected( $post_id, $direction = 'to', $post_type = 'any', $output = 'ids' ) {
|
114 |
+
$ids = Posts2Posts::get_connected( $post_id, $direction );
|
|
|
115 |
|
116 |
+
if ( empty( $ids ) )
|
117 |
+
return array();
|
118 |
|
119 |
+
if ( 'any' == $post_type && 'ids' == $output )
|
120 |
+
return $ids;
|
121 |
|
122 |
$args = array(
|
123 |
+
'post__in' => $ids,
|
124 |
'post_type'=> $post_type,
|
125 |
+
'post_status' => 'any',
|
126 |
'nopaging' => true,
|
127 |
);
|
|
|
128 |
|
129 |
+
$posts = get_posts( $args );
|
130 |
+
|
131 |
+
if ( 'objects' == $output )
|
132 |
+
return $posts;
|
133 |
|
134 |
+
foreach ( $posts as &$post )
|
135 |
+
$post = $post->ID;
|
136 |
|
137 |
+
return $posts;
|
138 |
}
|
139 |
|
140 |
/**
|
141 |
+
* Display the list of connected posts as an unordered list
|
|
|
142 |
*
|
143 |
+
* @param array $args See p2p_get_connected()
|
144 |
*/
|
145 |
+
function p2p_list_connected( $post_id, $direction = 'to', $post_type = 'any' ) {
|
146 |
+
$posts = p2p_get_connected( $post_id, $direction, $post_type, 'objects' );
|
147 |
+
|
148 |
+
if ( empty( $posts ) )
|
149 |
+
return;
|
150 |
+
|
151 |
+
echo '<ul>';
|
152 |
+
foreach ( $posts as $post )
|
153 |
+
echo html( 'li', html_link( get_permalink( $post->ID ), get_the_title( $post->ID ) ) );
|
154 |
+
echo '</ul>';
|
155 |
}
|
156 |
|
core.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// Abstraction layer for connection storage
|
4 |
+
|
5 |
+
class Posts2Posts {
|
6 |
+
const TAX = 'p2p';
|
7 |
+
|
8 |
+
function init() {
|
9 |
+
add_action( 'init', array( __CLASS__, 'setup' ) );
|
10 |
+
add_action( 'delete_post', array( __CLASS__, 'delete_post' ) );
|
11 |
+
}
|
12 |
+
|
13 |
+
function setup() {
|
14 |
+
register_taxonomy( self::TAX, 'post', array( 'public' => false ) );
|
15 |
+
}
|
16 |
+
|
17 |
+
function delete_post( $post_id ) {
|
18 |
+
wp_delete_term( self::convert( 'term', $post_id ), self::TAX );
|
19 |
+
}
|
20 |
+
|
21 |
+
function connect( $post_a, $post_b ) {
|
22 |
+
if ( empty( $post_a ) )
|
23 |
+
return;
|
24 |
+
|
25 |
+
$terms = self::convert( 'term', $post_b );
|
26 |
+
|
27 |
+
if ( empty( $terms ) )
|
28 |
+
return;
|
29 |
+
|
30 |
+
wp_set_object_terms( $post_a, $terms, self::TAX, true );
|
31 |
+
}
|
32 |
+
|
33 |
+
function disconnect( $post_a, $post_b ) {
|
34 |
+
if ( empty( $post_a ) )
|
35 |
+
return;
|
36 |
+
|
37 |
+
$terms = self::convert( 'term', $post_b );
|
38 |
+
|
39 |
+
if ( empty( $terms ) )
|
40 |
+
return;
|
41 |
+
|
42 |
+
$list = wp_get_object_terms( $post_a, self::TAX, 'fields=names' );
|
43 |
+
|
44 |
+
wp_set_object_terms( $post_a, array_diff( $list, $terms ), self::TAX );
|
45 |
+
}
|
46 |
+
|
47 |
+
function is_connected( $post_a, $post_b ) {
|
48 |
+
$terms = self::convert( 'term', $post_b );
|
49 |
+
|
50 |
+
return is_object_in_term( $post_a, $terms, self::TAX );
|
51 |
+
}
|
52 |
+
|
53 |
+
function get_connected( $post_id, $direction ) {
|
54 |
+
if ( 'from' == $direction ) {
|
55 |
+
$terms = wp_get_object_terms( $post_id, self::TAX, array( 'fields' => 'names' ) );
|
56 |
+
return self::convert( 'post', $terms );
|
57 |
+
} else {
|
58 |
+
$term = get_term_by( 'slug', reset( self::convert( 'term', $post_id ) ), self::TAX )->term_id;
|
59 |
+
return get_objects_in_term( $term, self::TAX );
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
// Add a 'p' to avoid confusion with term ids
|
64 |
+
private function convert( $to, $ids ) {
|
65 |
+
$ids = array_filter( (array) $ids );
|
66 |
+
|
67 |
+
if ( 'term' == $to )
|
68 |
+
foreach ( $ids as &$id )
|
69 |
+
$id = 'p' . $id;
|
70 |
+
else
|
71 |
+
foreach ( $ids as &$id )
|
72 |
+
$id = substr( $id, 1 );
|
73 |
+
|
74 |
+
return $ids;
|
75 |
+
}
|
76 |
+
}
|
77 |
+
|
lang/posts-to-posts.pot
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
# Translation of the WordPress plugin Posts 2 Posts 0.
|
2 |
# Copyright (C) 2010 scribu
|
3 |
# This file is distributed under the same license as the Posts 2 Posts package.
|
4 |
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
|
@@ -6,9 +6,9 @@
|
|
6 |
#, fuzzy
|
7 |
msgid ""
|
8 |
msgstr ""
|
9 |
-
"Project-Id-Version: Posts 2 Posts 0.
|
10 |
"Report-Msgid-Bugs-To: http://wordpress.org/tag/posts-to-posts\n"
|
11 |
-
"POT-Creation-Date: 2010-
|
12 |
"PO-Revision-Date: 2010-MO-DA HO:MI+ZONE\n"
|
13 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
14 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
@@ -16,25 +16,25 @@ msgstr ""
|
|
16 |
"Content-Type: text/plain; charset=utf-8\n"
|
17 |
"Content-Transfer-Encoding: 8bit\n"
|
18 |
|
19 |
-
#: admin/admin.php:
|
20 |
msgid "Connected"
|
21 |
msgstr ""
|
22 |
|
23 |
-
#: admin/admin.php:
|
24 |
msgid "No connections."
|
25 |
msgstr ""
|
26 |
|
27 |
-
#: admin/admin.php:
|
28 |
msgid "Search"
|
29 |
msgstr ""
|
30 |
|
31 |
-
#: admin/admin.php:
|
32 |
msgid ""
|
33 |
"Start typing name of connected post type and click on it if you want to "
|
34 |
"connect it."
|
35 |
msgstr ""
|
36 |
|
37 |
-
#: admin/admin.php:
|
38 |
msgid ""
|
39 |
"Enter IDs of connected post types separated by commas, or turn on JavaScript!"
|
40 |
msgstr ""
|
1 |
+
# Translation of the WordPress plugin Posts 2 Posts 0.3-alpha2 by scribu.
|
2 |
# Copyright (C) 2010 scribu
|
3 |
# This file is distributed under the same license as the Posts 2 Posts package.
|
4 |
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
|
6 |
#, fuzzy
|
7 |
msgid ""
|
8 |
msgstr ""
|
9 |
+
"Project-Id-Version: Posts 2 Posts 0.3-alpha2\n"
|
10 |
"Report-Msgid-Bugs-To: http://wordpress.org/tag/posts-to-posts\n"
|
11 |
+
"POT-Creation-Date: 2010-08-04 20:37+0300\n"
|
12 |
"PO-Revision-Date: 2010-MO-DA HO:MI+ZONE\n"
|
13 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
14 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
16 |
"Content-Type: text/plain; charset=utf-8\n"
|
17 |
"Content-Transfer-Encoding: 8bit\n"
|
18 |
|
19 |
+
#: admin/admin.php:94
|
20 |
msgid "Connected"
|
21 |
msgstr ""
|
22 |
|
23 |
+
#: admin/admin.php:115
|
24 |
msgid "No connections."
|
25 |
msgstr ""
|
26 |
|
27 |
+
#: admin/admin.php:134
|
28 |
msgid "Search"
|
29 |
msgstr ""
|
30 |
|
31 |
+
#: admin/admin.php:142
|
32 |
msgid ""
|
33 |
"Start typing name of connected post type and click on it if you want to "
|
34 |
"connect it."
|
35 |
msgstr ""
|
36 |
|
37 |
+
#: admin/admin.php:152
|
38 |
msgid ""
|
39 |
"Enter IDs of connected post types separated by commas, or turn on JavaScript!"
|
40 |
msgstr ""
|
posts-to-posts.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<?php
|
2 |
/*
|
3 |
Plugin Name: Posts 2 Posts
|
4 |
-
Version: 0.
|
5 |
Plugin Author: scribu
|
6 |
Description: Create connections between posts of different types
|
7 |
Author URI: http://scribu.net/
|
@@ -26,16 +26,17 @@ You should have received a copy of the GNU General Public License
|
|
26 |
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
27 |
*/
|
28 |
|
29 |
-
define( 'P2P_META_KEY', '_p2p' );
|
30 |
-
|
31 |
require dirname( __FILE__ ) . '/scb/load.php';
|
32 |
|
33 |
function _p2p_init() {
|
|
|
34 |
require dirname( __FILE__ ) . '/api.php';
|
35 |
|
|
|
|
|
36 |
if ( is_admin() ) {
|
37 |
require dirname( __FILE__ ) . '/admin/admin.php';
|
38 |
-
|
39 |
}
|
40 |
}
|
41 |
scb_init( '_p2p_init' );
|
1 |
<?php
|
2 |
/*
|
3 |
Plugin Name: Posts 2 Posts
|
4 |
+
Version: 0.3
|
5 |
Plugin Author: scribu
|
6 |
Description: Create connections between posts of different types
|
7 |
Author URI: http://scribu.net/
|
26 |
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
27 |
*/
|
28 |
|
|
|
|
|
29 |
require dirname( __FILE__ ) . '/scb/load.php';
|
30 |
|
31 |
function _p2p_init() {
|
32 |
+
require dirname( __FILE__ ) . '/core.php';
|
33 |
require dirname( __FILE__ ) . '/api.php';
|
34 |
|
35 |
+
Posts2Posts::init();
|
36 |
+
|
37 |
if ( is_admin() ) {
|
38 |
require dirname( __FILE__ ) . '/admin/admin.php';
|
39 |
+
P2P_Admin::init( __FILE__ );
|
40 |
}
|
41 |
}
|
42 |
scb_init( '_p2p_init' );
|
readme.txt
CHANGED
@@ -4,26 +4,29 @@ Donate link: http://scribu.net/paypal
|
|
4 |
Tags: cms, custom post types, relationships, many-to-many
|
5 |
Requires at least: 3.0
|
6 |
Tested up to: 3.0
|
7 |
-
Stable tag: 0.
|
8 |
|
9 |
Create connections between posts
|
10 |
|
11 |
== Description ==
|
12 |
|
13 |
-
This plugin allows you to create relationships between posts of different types. The relationships are stored in
|
14 |
|
15 |
-
To register a connection
|
16 |
|
17 |
`
|
18 |
function my_connection_types() {
|
19 |
-
|
|
|
|
|
|
|
|
|
20 |
}
|
21 |
add_action('init', 'my_connection_types', 100);
|
22 |
`
|
23 |
<br>
|
24 |
|
25 |
-
|
26 |
-
|
27 |
|
28 |
== Installation ==
|
29 |
|
@@ -47,9 +50,13 @@ Make sure your host is running PHP 5. The only foolproof way to do this is to ad
|
|
47 |
|
48 |
== Changelog ==
|
49 |
|
|
|
|
|
|
|
|
|
50 |
= 0.2 =
|
51 |
-
* added p2p_list_connected() template tag
|
52 |
* UI that supports multiple related posts. props [Patrik Bón](http://www.mrhead.sk/)
|
|
|
53 |
* [more info](http://scribu.net/wordpress/posts-to-posts/p2p-0-2.html)
|
54 |
|
55 |
= 0.1 =
|
4 |
Tags: cms, custom post types, relationships, many-to-many
|
5 |
Requires at least: 3.0
|
6 |
Tested up to: 3.0
|
7 |
+
Stable tag: 0.3
|
8 |
|
9 |
Create connections between posts
|
10 |
|
11 |
== Description ==
|
12 |
|
13 |
+
This plugin allows you to create relationships between posts of different types. The relationships are stored in a hidden taxonomy.
|
14 |
|
15 |
+
To register a connection type, add this code in your theme's functions.php file:
|
16 |
|
17 |
`
|
18 |
function my_connection_types() {
|
19 |
+
if ( !function_exists('p2p_register_connection_type') )
|
20 |
+
return;
|
21 |
+
|
22 |
+
p2p_register_connection_type('book', 'author');
|
23 |
+
p2p_register_connection_type('library', 'book');
|
24 |
}
|
25 |
add_action('init', 'my_connection_types', 100);
|
26 |
`
|
27 |
<br>
|
28 |
|
29 |
+
Links: [API](http://plugins.trac.wordpress.org/browser/posts-to-posts/tags/0.3/api.php) | [Plugin News](http://scribu.net/wordpress/posts-to-posts) | [Author's Site](http://scribu.net)
|
|
|
30 |
|
31 |
== Installation ==
|
32 |
|
50 |
|
51 |
== Changelog ==
|
52 |
|
53 |
+
= 0.3 =
|
54 |
+
* store connections using a taxonomy instead of postmeta
|
55 |
+
* [more info](http://scribu.net/wordpress/posts-to-posts/p2p-0-3.html)
|
56 |
+
|
57 |
= 0.2 =
|
|
|
58 |
* UI that supports multiple related posts. props [Patrik Bón](http://www.mrhead.sk/)
|
59 |
+
* added p2p_list_connected() template tag
|
60 |
* [more info](http://scribu.net/wordpress/posts-to-posts/p2p-0-2.html)
|
61 |
|
62 |
= 0.1 =
|
scb/Query.php
DELETED
@@ -1,75 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// A decorator for the WP_Query class
|
4 |
-
class scbQuery {
|
5 |
-
protected $wp_query;
|
6 |
-
|
7 |
-
public function __construct( $qv = '', $debug = false ) {
|
8 |
-
if ( $debug )
|
9 |
-
add_filter( 'posts_request', array( $this, '_debug' ), 999 );
|
10 |
-
|
11 |
-
if ( empty( $qv ) ) {
|
12 |
-
$this->_add_filters( true );
|
13 |
-
} else {
|
14 |
-
$this->_add_filters();
|
15 |
-
$this->wp_query = new WP_Query( $qv );
|
16 |
-
$this->_remove_filters();
|
17 |
-
}
|
18 |
-
}
|
19 |
-
|
20 |
-
/*
|
21 |
-
final public function __get( $key ) {
|
22 |
-
return $this->wp_query->$key;
|
23 |
-
}
|
24 |
-
|
25 |
-
final public function __call( $name, $args ) {
|
26 |
-
return call_user_func_array( array( $this->wp_query, $name ), $args );
|
27 |
-
}
|
28 |
-
*/
|
29 |
-
|
30 |
-
public function _debug( $query ) {
|
31 |
-
remove_filter( current_filter(), array( $this, __FUNCTION__ ), 999 );
|
32 |
-
|
33 |
-
debug( $query );
|
34 |
-
|
35 |
-
return $query;
|
36 |
-
}
|
37 |
-
|
38 |
-
public function _add_filters( $runonce = false ) {
|
39 |
-
if ( $runonce )
|
40 |
-
foreach ( $this->_find_filters() as $filter )
|
41 |
-
add_filter( $filter, array( $this, '_dispatch' ), 10, 10 );
|
42 |
-
else
|
43 |
-
foreach ( $this->_find_filters() as $filter )
|
44 |
-
add_filter( $filter, array( $this, $filter ), 10, 10 );
|
45 |
-
}
|
46 |
-
|
47 |
-
public function _dispatch( $value ) {
|
48 |
-
$filter = current_filter();
|
49 |
-
|
50 |
-
remove_filter( $filter, array( $this, '_dispatch' ), 10, 10 );
|
51 |
-
|
52 |
-
return $this->$filter( $value );
|
53 |
-
}
|
54 |
-
|
55 |
-
public function _remove_filters() {
|
56 |
-
foreach ( $this->_find_filters() as $filter )
|
57 |
-
remove_filter( $filter, array( $this, $filter ), 10, 10 );
|
58 |
-
}
|
59 |
-
|
60 |
-
private function _find_filters() {
|
61 |
-
$filters = array();
|
62 |
-
|
63 |
-
foreach ( _scb_get_public_methods( $this ) as $method )
|
64 |
-
if ( '_' != substr( $method, 0, 1 ) )
|
65 |
-
$filters[] = $method;
|
66 |
-
|
67 |
-
return $filters;
|
68 |
-
}
|
69 |
-
}
|
70 |
-
|
71 |
-
// Current scope is lost while calling external function
|
72 |
-
function _scb_get_public_methods( $class ) {
|
73 |
-
return get_class_methods( $class );
|
74 |
-
}
|
75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scb/QueryManipulation.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class scbQueryManipulation {
|
4 |
+
|
5 |
+
private $bits = array();
|
6 |
+
private $wp_query;
|
7 |
+
|
8 |
+
private static $filters = array(
|
9 |
+
'posts_where',
|
10 |
+
'posts_join',
|
11 |
+
'posts_groupby',
|
12 |
+
'posts_orderby',
|
13 |
+
'posts_distinct',
|
14 |
+
'post_limits',
|
15 |
+
'posts_fields'
|
16 |
+
);
|
17 |
+
|
18 |
+
public function __construct( $callback, $once = true ) {
|
19 |
+
$this->callback = $callback;
|
20 |
+
|
21 |
+
$this->enable();
|
22 |
+
|
23 |
+
if ( !$once )
|
24 |
+
return;
|
25 |
+
|
26 |
+
add_filter( 'posts_request', array( $this, '_disable' ) );
|
27 |
+
}
|
28 |
+
|
29 |
+
function _disable( $request ) {
|
30 |
+
remove_filter( 'posts_request', array( $this, '_disable' ) );
|
31 |
+
|
32 |
+
$this->disable();
|
33 |
+
|
34 |
+
return $request;
|
35 |
+
}
|
36 |
+
|
37 |
+
public function enable() {
|
38 |
+
foreach ( self::$filters as $filter ) {
|
39 |
+
add_filter( $filter, array( $this, 'collect' ), 999, 2 );
|
40 |
+
add_filter( $filter . '_request' , array( $this, 'update' ), 9 );
|
41 |
+
}
|
42 |
+
|
43 |
+
add_action( 'posts_selection' , array( $this, 'alter' ) );
|
44 |
+
}
|
45 |
+
|
46 |
+
public function disable() {
|
47 |
+
foreach ( self::$filters as $filter ) {
|
48 |
+
remove_filter( $filter, array( $this, 'collect' ), 999, 2 );
|
49 |
+
remove_filter( $filter . '_request' , array( $this, 'update' ), 9 );
|
50 |
+
}
|
51 |
+
|
52 |
+
remove_action( 'posts_selection' , array( $this, 'alter' ) );
|
53 |
+
}
|
54 |
+
|
55 |
+
function collect( $value, $wp_query ) {
|
56 |
+
// remove 'posts_'
|
57 |
+
$key = explode( '_', current_filter() );
|
58 |
+
$key = array_slice( $key, 1 );
|
59 |
+
$key = implode( '_', $key );
|
60 |
+
|
61 |
+
$this->bits[ $key ] = $value;
|
62 |
+
|
63 |
+
$this->wp_query = $wp_query;
|
64 |
+
|
65 |
+
return $value;
|
66 |
+
}
|
67 |
+
|
68 |
+
function alter( $query ) {
|
69 |
+
$this->bits = call_user_func( $this->callback, $this->bits, $this->wp_query );
|
70 |
+
}
|
71 |
+
|
72 |
+
function update( $value ) {
|
73 |
+
// remove 'posts_' and '_request'
|
74 |
+
$key = explode( '_', current_filter() );
|
75 |
+
$key = array_slice( $key, 1, -1 );
|
76 |
+
$key = implode( '_', $key );
|
77 |
+
|
78 |
+
return $this->bits[ $key ];
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
scb/Util.php
CHANGED
@@ -117,8 +117,16 @@ class scbUtil {
|
|
117 |
|
118 |
|
119 |
if ( ! function_exists( 'html' ) ):
|
120 |
-
function html(
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
|
123 |
return "<{$tag}>{$content}</{$closing}>";
|
124 |
}
|
117 |
|
118 |
|
119 |
if ( ! function_exists( 'html' ) ):
|
120 |
+
function html($tag, $attributes = array(), $content = '') {
|
121 |
+
if ( is_array( $attributes ) ) {
|
122 |
+
$closing = $tag;
|
123 |
+
foreach ( $attributes as $key => $value ) {
|
124 |
+
$tag .= ' ' . $key . '="' . esc_attr( $value ) . '"';
|
125 |
+
}
|
126 |
+
} else {
|
127 |
+
$content = $attributes;
|
128 |
+
list($closing) = explode(' ', $tag, 2);
|
129 |
+
}
|
130 |
|
131 |
return "<{$tag}>{$content}</{$closing}>";
|
132 |
}
|
scb/load.php
CHANGED
@@ -1,9 +1,10 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
$GLOBALS['_scb_data'] = array(
|
4 |
'scbUtil', 'scbOptions', 'scbForms', 'scbTable',
|
5 |
'scbWidget', 'scbAdminPage', 'scbBoxesPage',
|
6 |
-
'
|
|
|
7 |
|
8 |
if ( !class_exists( 'scbLoad4' ) ) :
|
9 |
class scbLoad4 {
|
1 |
<?php
|
2 |
|
3 |
+
$GLOBALS['_scb_data'] = array( 23, __FILE__, array(
|
4 |
'scbUtil', 'scbOptions', 'scbForms', 'scbTable',
|
5 |
'scbWidget', 'scbAdminPage', 'scbBoxesPage',
|
6 |
+
'scbQueryManipulation', 'scbRewrite', 'scbCron',
|
7 |
+
) );
|
8 |
|
9 |
if ( !class_exists( 'scbLoad4' ) ) :
|
10 |
class scbLoad4 {
|