admin-ui.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. <?php
  2. /**
  3. * Provides the core admin UI (Tools > Keyring) which includes:
  4. * - managing Service credentials
  5. * - creating connections
  6. * - deleting connections
  7. * - (coming soon) managing active/inactive Services
  8. *
  9. * Run Keyring with KEYRING__HEADLESS_MODE defined as true to disable all UI.
  10. *
  11. * @package Keyring
  12. */
  13. class Keyring_Admin_UI {
  14. var $keyring = false;
  15. function __construct() {
  16. add_action( 'admin_menu', array( $this, 'admin_menu' ) );
  17. }
  18. static function &init() {
  19. static $instance = false;
  20. if ( !$instance ) {
  21. $instance = new Keyring_Admin_UI;
  22. }
  23. return $instance;
  24. }
  25. function inline_css() {
  26. ?><style type="text/css">
  27. .wrap ul li {
  28. list-style-type: square;
  29. margin: .3em 0 .3em 2em;
  30. }
  31. </style><?php
  32. }
  33. function admin_menu() {
  34. $hook = add_management_page( 'Keyring', 'Keyring', 'read', 'keyring', array( $this, 'admin_page' ), '' );
  35. add_action( "load-$hook", array( $this, 'admin_page_load' ) );
  36. }
  37. function admin_page_load() {
  38. $this->keyring = Keyring::init();
  39. add_action( 'admin_head', array( $this, 'inline_css' ) );
  40. }
  41. function admin_page_header( $screen = false ) {
  42. // Output the actual heading + icon for the page
  43. echo '<div class="wrap">';
  44. screen_icon( 'ms-admin' );
  45. switch ( $screen ) {
  46. case 'tokens' :
  47. echo '<h2>' . __( 'Keyring: Managed Connections', 'keyring' ) . ' <a href="' . Keyring_Util::admin_url( false, array( 'action' => 'services' ) ) . '" class="add-new-h2">' . __( 'Add New', 'keyring' ) . '</a></h2>';
  48. break;
  49. case 'services' :
  50. echo '<h2>' . __( 'Add New Connection', 'keyring' ) . '</h2>';
  51. echo '<p><a href="' . Keyring_Util::admin_url() . '">' . __( '&larr; Back', 'keyring' ) . '</a></p>';
  52. break;
  53. case 'error' :
  54. echo '<h2>' . __( 'Keyring Error!', 'keyring' ) . '</h2>';
  55. break;
  56. default :
  57. echo '<h2>' . __( 'Keyring', 'keyring' ) . '</h2>';
  58. }
  59. // Output any errors if we have them, then stop, and link back to home.
  60. if ( $this->keyring->has_errors() ) {
  61. echo '<div id="keyring-admin-errors" class="updated"><ul>';
  62. foreach ( $this->keyring->get_errors() as $error ) {
  63. echo "<li>" . esc_html( $error ) . "</li>";
  64. }
  65. echo '</ul></div>';
  66. echo '<p class="submit"><a href="' . Keyring_Util::admin_url( $_REQUEST['service'] ) . '" class="button-primary">' . __( 'Start Again', 'keyring' ) . '</a></p>';
  67. return;
  68. }
  69. // Output any messages as part of the UI (don't abort).
  70. if ( $this->keyring->has_messages() ) {
  71. echo '<div id="keyring-admin-messages" class="updated"><ul>';
  72. foreach ( $this->keyring->get_messages() as $message ) {
  73. echo "<li>" . esc_html( $message ) . "</li>";
  74. }
  75. echo '</ul></div>';
  76. }
  77. }
  78. static function admin_page_footer() {
  79. echo '</div>'; // class="wrap"
  80. }
  81. function admin_page() {
  82. // Handle delete request. Will default back to "tokens" later
  83. if ( isset( $_REQUEST['action'] ) && 'delete' == $_REQUEST['action'] ) {
  84. if ( !isset( $_REQUEST['nonce'] ) || !wp_verify_nonce( $_REQUEST['nonce'], 'keyring-delete-' . $_REQUEST['service'] . '-' . $_REQUEST['token'] ) ) {
  85. Keyring::error( __( 'Invalid/missing delete nonce.', 'keyring' ) );
  86. exit;
  87. }
  88. if ( $this->keyring->get_token_store()->delete( array( 'id' => (int) $_REQUEST['token'], 'type' => 'access' ) ) )
  89. Keyring::message( __( 'That connection has been deleted.', 'keyring' ) );
  90. else
  91. Keyring::message( __( 'Could not delete that connection!', 'keyring' ) );
  92. }
  93. // Handle test request. Will default back to "tokens" later
  94. if ( isset( $_REQUEST['action'] ) && 'test' == $_REQUEST['action'] ) {
  95. if ( !isset( $_REQUEST['nonce'] ) || !wp_verify_nonce( $_REQUEST['nonce'], 'keyring-test-' . $_REQUEST['service'] . '-' . $_REQUEST['token'] ) ) {
  96. Keyring::error( __( 'Invalid/missing testing nonce.', 'keyring' ) );
  97. exit;
  98. }
  99. // If the test_connection() method exists, call it for this service/connection
  100. $service = $this->keyring->get_service_by_name( $_REQUEST['service'] );
  101. if ( method_exists( $service, 'test_connection' ) ) {
  102. $service->set_token( $this->keyring->get_token_store()->get_token( array( 'id' => $_REQUEST['token'], 'type' => 'request' ) ) );
  103. if ( $service->test_connection() ) {
  104. Keyring::message( __( 'This connection is working correctly.', 'keyring' ) );
  105. } else {
  106. Keyring::message( __( 'This connection is <strong>NOT</strong> working correctly.', 'keyring' ) );
  107. }
  108. } else {
  109. Keyring::message( __( 'This service does not currently support connection testing.', 'keyring' ) );
  110. }
  111. }
  112. // Set up our defaults
  113. $service = '';
  114. if ( !empty( $_REQUEST['service'] ) )
  115. $service = $_REQUEST['service'];
  116. $action = 'tokens';
  117. if ( isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], array( 'tokens', 'services', 'request', 'verify', 'manage' ) ) )
  118. $action = $_REQUEST['action'];
  119. // Custom UI optionally hooked in to handle this service/action. Trigger action
  120. // and assume it handles everything, so bail out after that.
  121. if ( Keyring_Util::has_custom_ui( $service, $action ) ) {
  122. do_action( "keyring_{$service}_{$action}_ui" );
  123. return;
  124. }
  125. // Nothing else has bailed, so it must be one of our default/core screens.
  126. switch ( $action ) {
  127. case 'tokens' :
  128. $this->admin_page_header( 'tokens' );
  129. $list_table = new Keyring_Connections_List_Table();
  130. $list_table->display();
  131. $this->admin_page_footer();
  132. break;
  133. case 'services' :
  134. $this->admin_page_header( 'services' );
  135. $services = $this->keyring->get_registered_services();
  136. if ( count( $services ) ) {
  137. $configured = $not_configured = array();
  138. foreach ( $services as $service ) {
  139. if ( $service->is_configured() )
  140. $configured[] = $service;
  141. else
  142. $not_configured[] = $service;
  143. }
  144. if ( count( $configured ) ) {
  145. echo '<p><strong>' . __( 'Click a service to create a new connection:', 'keyring' ) . '</strong></p>';
  146. echo '<ul>';
  147. foreach ( $configured as $service ) {
  148. $request_kr_nonce = wp_create_nonce( 'keyring-request' );
  149. $request_nonce = wp_create_nonce( 'keyring-request-' . $service->get_name() );
  150. echo '<li><a href="' . esc_url( Keyring_Util::admin_url( $service->get_name(), array( 'action' => 'request', 'kr_nonce' => $request_kr_nonce, 'nonce' => $request_nonce ) ) ) . '">' . esc_html( $service->get_label() ) . '</a>';
  151. if ( has_action( 'keyring_' . $service->get_name() . '_manage_ui' ) ) {
  152. $manage_kr_nonce = wp_create_nonce( 'keyring-manage' );
  153. $manage_nonce = wp_create_nonce( 'keyring-manage-' . $service->get_name() );
  154. echo ' (<a href="' . esc_url( Keyring_Util::admin_url( $service->get_name(), array( 'action' => 'manage', 'kr_nonce' => $manage_kr_nonce, 'nonce' => $manage_nonce ) ) ) . '">' . esc_html( __( 'Manage', 'keyring' ) ) . '</a>)';
  155. }
  156. echo '</li>';
  157. }
  158. echo '</ul><br /><br />';
  159. } else {
  160. echo '<p>' . __( 'There are no fully-configured services available to connect to.', 'keyring' ) . '</p>';
  161. }
  162. if ( count( $not_configured ) ) {
  163. echo '<p>' . __( 'The following services need to be configured correctly before you can connect to them.', 'keyring' ) . '</p>';
  164. echo '<ul>';
  165. foreach ( $not_configured as $service ) {
  166. if ( !has_action( 'keyring_' . $service->get_name() . '_manage_ui' ) )
  167. continue;
  168. $manage_kr_nonce = wp_create_nonce( 'keyring-manage' );
  169. $manage_nonce = wp_create_nonce( 'keyring-manage-' . $service->get_name() );
  170. echo '<li><a href="' . esc_url( Keyring_Util::admin_url( $service->get_name(), array( 'action' => 'manage', 'kr_nonce' => $manage_kr_nonce, 'nonce' => $manage_nonce ) ) ) . '">' . esc_html( $service->get_label() ) . '</a></li>';
  171. }
  172. echo '</ul>';
  173. }
  174. }
  175. $this->admin_page_footer();
  176. break;
  177. }
  178. }
  179. }
  180. /** WordPress List Table Administration API and base class */
  181. require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
  182. class Keyring_Connections_List_Table extends WP_List_Table {
  183. var $keyring = false;
  184. function __construct() {
  185. $this->keyring = Keyring::init();
  186. parent::__construct( array(
  187. 'singular' => 'connection',
  188. 'plural' => 'connections',
  189. 'screen' => $this->keyring->admin_page,
  190. ) );
  191. $this->items = Keyring::get_token_store()->get_tokens();
  192. }
  193. function no_items() {
  194. echo '<p>' . sprintf( __( 'You haven\'t added any connections yet. <a href="%s">Add a New Connection</a>.', 'keyring' ), esc_url( Keyring_Util::admin_url( false, array( 'action' => 'services' ) ) ) ) . '</p>';
  195. }
  196. function get_columns() {
  197. return array(
  198. 'service' => __( 'Service', 'keyring' ),
  199. 'avatar' => __( 'Avatar', 'keyring' ),
  200. 'id' => __( 'External ID', 'keyring' ),
  201. 'name' => __( 'Name', 'keyring' ),
  202. 'actions' => '&nbsp;'
  203. );
  204. }
  205. function column_service( $row ) {
  206. echo $row->get_service()->get_label();
  207. }
  208. function column_avatar( $row ) {
  209. $picture = $row->get_meta( 'picture' );
  210. if ( $picture ) {
  211. echo '<img src="' . esc_attr( $picture ) . '" width="40" height="40" border="1" alt="' . __( 'Avatar', 'keyring' ) . '" />';
  212. } else {
  213. echo '-';
  214. }
  215. }
  216. function column_id( $row ) {
  217. echo $row->get_meta( 'user_id' );
  218. }
  219. function column_name( $row ) {
  220. // Make a few attempts to get something to display here
  221. $name = $row->get_meta( 'name' );
  222. if ( !$name )
  223. $name = $row->get_meta( 'username' );
  224. if ( !$name )
  225. $name = trim( $row->get_meta( 'first_name' ) . ' ' . $row->get_meta( 'last_name' ) );
  226. if ( $name )
  227. echo $name;
  228. else
  229. echo '-';
  230. }
  231. function column_actions( $row ) {
  232. $kr_delete_nonce = wp_create_nonce( 'keyring-delete' );
  233. $delete_nonce = wp_create_nonce( 'keyring-delete-' . $row->get_service()->get_name() . '-' . $row->get_uniq_id() );
  234. $kr_test_nonce = wp_create_nonce( 'keyring-test' );
  235. $test_nonce = wp_create_nonce( 'keyring-test-' . $row->get_service()->get_name() . '-' . $row->get_uniq_id() );
  236. echo '<span class="row-actions">';
  237. echo '<span class="trash"><a href="' . Keyring_Util::admin_url( false, array( 'action' => 'delete', 'service' => $row->get_service()->get_name(), 'token' => $row->get_uniq_id(), 'kr_nonce' => $kr_delete_nonce, 'nonce' => $delete_nonce ) ) . '" title="' . esc_attr( __( 'Delete', 'keyring' ) ) . '" class="delete">Delete</a></span>';
  238. echo ' | ';
  239. echo '<a href="' . Keyring_Util::admin_url( false, array( 'action' => 'test', 'service' => $row->get_service()->get_name(), 'token' => $row->get_uniq_id(), 'kr_nonce' => $kr_test_nonce, 'nonce' => $test_nonce ) ) . '" title="' . esc_attr( __( 'Test', 'keyring' ) ) . '" class="test">Test</a>';
  240. echo '</span>';
  241. }
  242. }