Want more features on Pastebin? Sign Up, it's FREE!

Patched allow-multiple-accounts.php(Allow Multiple Accounts)

By: jtarrier on Jun 13th, 2011  |  syntax: PHP  |  size: 20.30 KB  |  views: 1,191  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. <?php
  2. /**
  3.  * @package Allow_Multiple_Accounts
  4.  * @author Scott Reilly
  5.  * @version 2.0.1
  6.  */
  7. /*
  8. Plugin Name: Allow Multiple Accounts
  9. Version: 2.0.1
  10. Plugin URI: http://coffee2code.com/wp-plugins/allow-multiple-accounts/
  11. Author: Scott Reilly
  12. Author URI: http://coffee2code.com
  13. Text Domain: allow-multiple-accounts
  14. Description: Allow multiple user accounts to be created from the same email address.
  15.  
  16. Compatible with WordPress 2.8+, 2.9+, 3.0+.
  17.  
  18. =>> Read the accompanying readme.txt file for instructions and documentation.
  19. =>> Also, visit the plugin's homepage for additional information and updates.
  20. =>> Or visit: http://wordpress.org/extend/plugins/allow-multiple-accounts/
  21.  
  22. */
  23.  
  24. /*
  25. Copyright (c) 2008-2010 by Scott Reilly (aka coffee2code)
  26.  
  27. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
  28. files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
  29. modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
  30. Software is furnished to do so, subject to the following conditions:
  31.  
  32. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  33.  
  34. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  35. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  36. LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  37. IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  38. */
  39.  
  40. if ( !class_exists( 'AllowMultipleAccounts' ) ) :
  41.  
  42. require_once( 'c2c-plugin.php' );
  43.  
  44. class AllowMultipleAccounts extends C2C_Plugin_016 {
  45.  
  46.         var $allow_multiple_accounts = false;  // Used internally; not a setting!
  47.         var $exceeded_limit = false;
  48.         var $retrieve_password_for = '';
  49.         var $during_user_creation = false; // part of a hack
  50.  
  51.         /**
  52.          * Constructor
  53.          */
  54.         function AllowMultipleAccounts() {
  55.                 $this->C2C_Plugin_016( '2.0.1', 'allow-multiple-accounts', 'c2c', __FILE__, array( 'settings_page' => 'users' ) );
  56.         }
  57.  
  58.         /**
  59.          * Initializes the plugin's config data array.
  60.          *
  61.          * @return void
  62.          */
  63.         function load_config() {
  64.                 $this->name = __( 'Allow Multiple Accounts', $this->textdomain );
  65.                 $this->menu_name = __( 'Multiple Accounts', $this->textdomain );
  66.  
  67.                 $this->config = array(
  68.                         'allow_for_everyone' => array('input' => 'checkbox', 'default' => true,
  69.                                         'label' => __( 'Allow multiple accounts for everyone?', $this->textdomain ),
  70.                                         'help' => __( 'If not checked, only the emails listed below can have multiple accounts.', $this->textdomain ) ),
  71.                         'account_limit' => array( 'input' => 'text', 'default' => '',
  72.                                         'label' => __( 'Account limit', $this->textdomain ),
  73.                                         'help' => __( 'The maximum number of accounts that can be associated with a single email address.  Leave blank to indicate no limit.', $this->textdomain ) ),
  74.                         'emails' => array( 'input' => 'inline_textarea', 'datatype' => 'array', 'default' => '',
  75.                                         'input_attributes' => 'style="width:98%;" rows="6"',
  76.                                         'label' => __( 'Multi-account emails', $this->textdomain ),
  77.                                         'help' => __( 'If the checkbox above is unchecked, then only the emails listed here will be allowed to have multiple accounts.  Define one per line.', $this->textdomain ) )
  78.                 );
  79.         }
  80.  
  81.         /**
  82.          * Override the plugin framework's register_filters() to actually actions against filters.
  83.          *
  84.          * @return void
  85.          */
  86.         function register_filters() {
  87.                 add_action( 'check_passwords', array( &$this, 'hack_check_passwords' ) );
  88.                 add_filter( 'pre_user_display_name', array( &$this, 'hack_pre_user_email' ) );
  89.                 add_filter( 'pre_user_email', array( &$this, 'hack_pre_user_email' ) );
  90.                 add_action( 'register_post', array( &$this, 'register_post' ), 1, 3 );
  91.                 add_filter( 'registration_errors', array( &$this, 'registration_errors' ), 1 );
  92.                 add_action( 'retrieve_password', array( &$this, 'retrieve_password' ) );
  93.                 add_filter( 'retrieve_password_message', array( &$this, 'retrieve_password_message' ) );
  94.                 add_action( 'user_profile_update_errors', array( &$this, 'user_profile_update_errors' ), 1, 3 );
  95.                 add_action( $this->get_hook( 'after_settings_form' ), array( &$this, 'list_multiple_accounts' ) );
  96. /*
  97.  * Patch to fix "Fatal error: Call to undefined function user_row() in .../wp-content/plugins/allow-multiple-accounts/allow-multiple-accounts.php on line 220
  98.  * 2010-03-09 J P Tarrier
  99.  */
  100.                 add_action( 'user_row', array( &$this, 'user_row' ), 1, 3 );
  101.         }
  102.  
  103.         /**
  104.          * Outputs the text above the setting form
  105.          *
  106.          * @return void (Text will be echoed.)
  107.          */
  108.         function options_page_description() {
  109.                 $options = $this->get_options();
  110.                 parent::options_page_description( __( 'Allow Multiple Accounts Settings', $this->textdomain ) );
  111.                 echo '<p>' . __( 'Allow multiple user accounts to be created from the same email address.', $this->textdomain ) . '</p>';
  112.                 echo '<p>' . __( 'By default, WordPress only allows a single user account to be assigned to a specific email address.  This plugin removes that restriction.  A setting is also provided to allow only certain email addresses the ability to have multiple accounts.  You may also specify a limit to the number of accounts an email address can have.', $this->textdomain ) . '</p>';
  113.                 echo '<p><a href="#multiaccount_list">' . __( 'View a list of user accounts grouped by email address.', $this->textdomain ) . '</a></p>';
  114.         }
  115.  
  116.         /**
  117.          * This is a HACK because WP 3.0 introduced a change that made it impossible to suppress the unique email check when creating a new user.
  118.          *
  119.          * For the hack, this filter is invoked just after wp_insert_user() checks for the uniqueness of the email address.  What this
  120.          * is doing is unsetting the flag by the get_user_by_email() overridden by this plugin, so that when called in any other context than
  121.          * wp_insert_user(), it'll actually get the user by email.
  122.          *
  123.          * @since 2.0
  124.          *
  125.          * @param string $display_name Display name for user
  126.          * @return string The same value as passed to the function
  127.          */
  128.         function hack_pre_user_display_name( $display_name ) {
  129.                 $this->during_user_creation = false;
  130.                 return $display_name;
  131.         }
  132.  
  133.         /**
  134.          * This is a HACK because WP 3.0 introduced a change that made it impossible to suppress the unique email check when creating a new user.
  135.          *
  136.          * For the hack, this filter is invoked just before wp_insert_user() checks for the uniqueness of the email address.  What this
  137.          * is doing is setting a flag so that the get_user_by_email() overridden by this plugin, when called in the wp_insert_user() context,
  138.          * knows to return false, making WP think the email address isn't in use.
  139.          *
  140.          * @since 2.0
  141.          *
  142.          * @param string $email Email for the user
  143.          * @return string The same value as passed to the function
  144.          */
  145.         function hack_pre_user_email( $email ) {
  146.                 $this->during_user_creation = true;
  147.                 return $email;
  148.         }
  149.  
  150.         /**
  151.          * This is a HACK because WP 3.0 introduced a change that made it impossible to suppress the unique email check when creating a new user.
  152.          *
  153.          * For the hack, this filter is invoked just before edit_user() does a bunch of error checks.  What this
  154.          * is doing is setting a flag so that the get_user_by_email() overridden by this plugin, when called in the edit_user() context,
  155.          * knows to return false, making WP think the email address isn't in use.
  156.          *
  157.          * @since 2.0
  158.          *
  159.          * @param string $user_login User login
  160.          * @return void
  161.          */
  162.         function hack_check_passwords( $user_login ) {
  163.                 $this->during_user_creation = true;
  164.         }
  165.  
  166.         /**
  167.          * Outputs a list of all user email addresses and their associated accounts.
  168.          *
  169.          * @return void (Text is echoed.)
  170.          */
  171.         function list_multiple_accounts() {
  172.                 global $wpdb;
  173.                 $users = $wpdb->get_results( "SELECT ID, user_email FROM $wpdb->users ORDER BY user_login" );
  174.                 $by_email = array();
  175.                 foreach ( $users as $user )
  176.                         $by_email[$user->user_email][] = $user;
  177.                 $emails = array_keys( $by_email );
  178.                 sort( $emails );
  179.                 $style = '';
  180.  
  181.                 echo <<<END
  182.                         <style type="text/css">
  183.                                 .emailrow {
  184.                                         background-color:#ffffef;
  185.                                 }
  186.                                 .check-column {
  187.                                         display:none;
  188.                                 }
  189.                         </style>
  190.                         <div class='wrap'><a name='multiaccount_list'></a>
  191.                                 <h2>
  192.  
  193. END;
  194.                 echo __( 'E-mail Addresses with Multiple User Accounts', $this->textdomain );
  195.                 echo <<<END
  196.                                 </h2>
  197.                                 <table class="widefat">
  198.                                 <thead>
  199.                                 <tr class="thead">
  200.  
  201. END;
  202.                 echo '<th>' . __( 'Username', $this->textdomain ) . '</th>' .
  203.                          '<th>' . __( 'Name', $this->textdomain ) . '</th>' .
  204.                          '<th>' . __( 'E-mail', $this->textdomain ) . '</th>' .
  205.                          '<th>' . __( 'Role', $this->textdomain ) . '</th>' .
  206.                          '<th class="num">' . __( 'Posts', $this->textdomain ) . '</th>';
  207.                 echo <<<END
  208.                                 </tr>
  209.                                 </thead>
  210.                                 <tbody id="users" class="list:user user-list">
  211.  
  212. END;
  213.  
  214.                 foreach ( $emails as $email ) {
  215.                         $email_users = $by_email[$email];
  216.                         $count = count( $by_email[$email] );
  217.                         echo '<tr class="emailrow"><td colspan="6">';
  218.                         printf( _n( '%1$s &#8212; %2$d account', '%1$s &#8212; %2$d accounts', $count, $this->textdomain ), $email, $count );
  219.                         echo '</td></tr>';
  220.                         foreach ( $by_email[$email] as $euser ) {
  221.                                 $user_object = new WP_User($euser->ID);
  222.                                 $roles = $user_object->roles;
  223.                                 $role = array_shift( $roles );
  224.                                 $style = ( ' class="alternate"' == $style ) ? '' : ' class="alternate"';
  225.                                 echo "\n\t" . user_row( $user_object, $style, $role );
  226.                         }
  227.                 }
  228.  
  229.                 echo <<<END
  230.                                 </tbody>
  231.                                 </table>
  232.                         </div>
  233.  
  234. END;
  235.         }
  236.  
  237.         /**
  238.          * Indicates if the specified email address has exceeded its allowable number of accounts.
  239.          *
  240.          * @param string $email Email address
  241.          * @param int $user_id (optional) ID of existing user, if updating a user
  242.          * @return boolean True if the email address has exceeded its allowable number of accounts; false otherwise
  243.          */
  244.         function has_exceeded_limit( $email, $user_id = null ) {
  245.                 $has = false;
  246.                 $options = $this->get_options();
  247.                 if ( $options['account_limit'] ) {
  248.                         $limit = (int) $options['account_limit'];
  249.                         $count = $this->count_multiple_accounts( $email, $user_id );
  250.                         if ( $count >= $limit )
  251.                                 $has = true;
  252.                 }
  253.                 return $has;
  254.         }
  255.  
  256.         /**
  257.          * Returns a count of the number of users associated with the given email.
  258.          *
  259.          * @param string $email The email account
  260.          * @param int $user_id (optional) ID of existing user, if updating a user
  261.          * @return int The number of users associated with the given email
  262.          */
  263.         function count_multiple_accounts( $email, $user_id =  null ) {
  264.                 global $wpdb;
  265.                 $sql = "SELECT COUNT(*) AS count FROM $wpdb->users WHERE user_email = %s";
  266.                 if ( $user_id )
  267.                         $sql .= ' AND ID != %d';
  268.                 return (int) $wpdb->get_var( $wpdb->prepare( $sql, $email, $user_id ) );
  269.         }
  270.  
  271.         /**
  272.          * Returns the users associated with the given email.
  273.          *
  274.          * @param string $email The email account
  275.          * @return array All of the users associated with the given email
  276.          */
  277.         function get_users_by_email( $email ) {
  278.                 global $wpdb;
  279.                 return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->users WHERE user_email = %s", $email ) );
  280.         }
  281.  
  282.         /**
  283.          * Returns a boolean indicating if the given email is associated with more than one user account.
  284.          *
  285.          * @param string $email The email account
  286.          * @return bool True if the given email is associated with more than one user account; false otherwise
  287.          */
  288.         function has_multiple_accounts( $email ) {
  289.                 return $this->count_multiple_accounts( $email ) > 1 ? true : false;
  290.         }
  291.  
  292.         /**
  293.          * Handler for 'register_post' action.  Intercepts potential 'email_exists' error and sets flags for later use, pertaining to if
  294.          * multiple accounts are authorized for the email and/or if the email has exceeded its allocated number of accounts.
  295.          *
  296.          * @param string $user_login User login
  297.          * @param string $user_email User email
  298.          * @param WP_Error $errors Error object
  299.          * @param int $user_id (optional) ID of existing user, if updating a user
  300.          * @return void
  301.          */
  302.         function register_post( $user_login, $user_email, $errors, $user_id = null ) {
  303.                 $options = $this->get_options();
  304.                 if ( $errors->get_error_message( 'email_exists' ) &&
  305.                         ( $options['allow_for_everyone'] || in_array( $user_email, $options['emails'] ) ) ) {
  306.                         if ( $this->has_exceeded_limit( $user_email, $user_id ) )
  307.                                 $this->exceeded_limit = true;
  308.                         else
  309.                                 $this->allow_multiple_accounts = true;
  310.                 }
  311.         }
  312.  
  313.         /**
  314.          * Handler for 'registration_errors' action to add and/or remove registration errors as needed.
  315.          *
  316.          * @param WP_Error $errors Error object
  317.          * @return WP_Error The potentially modified error object
  318.          */
  319.         function registration_errors( $errors ) {
  320.                 if ( $this->exceeded_limit )
  321.                         $errors->add( 'exceeded_limit', __( '<strong>ERROR</strong>: Too many accounts are associated with this email, please choose another one.', $this->textdomain ) );
  322.                 if ( $this->allow_multiple_accounts || $this->exceeded_limit ) {
  323.                         unset( $errors->errors['email_exists'] );
  324.                         unset( $errors->error_data['email_exists'] );
  325.                 }
  326.                 return $errors;
  327.         }
  328.  
  329.         /**
  330.          * Roundabout way of determining what user account a password retrieval is being requested for since some of the actions/filters don't specify.
  331.          *
  332.          * @param string $user_login User login
  333.          * @return string The same value as passed to the function
  334.          */
  335.         function retrieve_password( $user_login ) {
  336.                 $this->retrieve_password_for = $user_login;
  337.                 return $user_login;
  338.         }
  339.  
  340.         /**
  341.          * Appends text at the end of a 'retrieve password' email to remind users what accounts they have associated with their email address.
  342.          *
  343.          * @param string $message The original email message
  344.          * @return string Potentially modified email message
  345.          */
  346.         function retrieve_password_message( $message ) {
  347.                 $user = get_user_by( 'login', $this->retrieve_password_for );
  348.                 if ( $this->has_multiple_accounts( $user->user_email ) ) {
  349.                         $message .= "\r\n\r\n";
  350.                         $message .= __( 'For your information, your e-mail address is also associated with the following accounts:', $this->textdomain ) . "\r\n\r\n";
  351.                         foreach ( $this->get_users_by_email( $user->user_email ) as $user ) {
  352.                                 $message .= "\t" . $user->user_login . "\r\n";
  353.                         }
  354.                         $message .= "\r\n";
  355.                         $message .= __( 'In order to reset the password for any of these (if you aren\'t already successfully in the middle of doing so already), you should specify the login when requesting a password reset rather than using your e-mail.', $this->textdomain ) . "\r\n\r\n";
  356.                 }
  357.                 return $message;
  358.         }
  359.  
  360.         /**
  361.          * Intercept possible email_exists errors during user updating, and also possibly add errors.
  362.          *
  363.          * @param WP_Error $errors Error object
  364.          * @param boolean $update Is this being invoked due to a user being updated?
  365.          * @param WP_User $user User object
  366.          */
  367.         function user_profile_update_errors( $errors, $update, $user ) {
  368.                 $this->during_user_creation = false; // Part of HACK to work around WP3.0.0 bug
  369.                 $user_id = $update ? $user->ID : null;
  370.                 $this->register_post( $user->user_login, $user->user_email, $errors, $user_id );
  371.                 $errors = $this->registration_errors( $errors );
  372.         }
  373. } // end AllowMultipleAccounts
  374.  
  375. $GLOBALS['c2c_allow_multiple_accounts'] = new AllowMultipleAccounts();
  376.  
  377. endif; // end if !class_exists()
  378.  
  379.  
  380.         //
  381.         /**
  382.          * *******************
  383.          * TEMPLATE FUNCTIONS
  384.          *
  385.          * Functions suitable for use in other themes and plugins
  386.          * *******************
  387.          */
  388.  
  389.         /**
  390.          * Returns a count of the number of users associated with the given email.
  391.          *
  392.          * @since 2.0
  393.          *
  394.          * @param string $email The email account
  395.          * @return int The number of users associated with the given email
  396.          */
  397.         if ( !function_exists( 'c2c_count_multiple_accounts' ) ) {
  398.                 function c2c_count_multiple_accounts( $email ) { return $GLOBALS['c2c_allow_multiple_accounts']->count_multiple_accounts( $email ); }
  399.         }
  400.  
  401.         /**
  402.          * Returns the users associated with the given email.
  403.          *
  404.          * @since 2.0
  405.          *
  406.          * @param string $email The email account
  407.          * @return array All of the users associated with the given email
  408.          */
  409.         if ( !function_exists( 'c2c_get_users_by_email' ) ) {
  410.                 function c2c_get_users_by_email( $email ) { return $GLOBALS['c2c_allow_multiple_accounts']->get_users_by_email( $email ); }
  411.         }
  412.  
  413.         /**
  414.          * Returns a boolean indicating if the given email is associated with more than one user account.
  415.          *
  416.          * @since 2.0
  417.          *
  418.          * @param string $email The email account
  419.          * @return bool True if the given email is associated with more than one user account; false otherwise
  420.          */
  421.         if ( !function_exists( 'c2c_has_multiple_accounts' ) ) {
  422.                 function c2c_has_multiple_accounts( $email ) { return $GLOBALS['c2c_allow_multiple_accounts']->has_multiple_accounts( $email ); }
  423.         }
  424.  
  425.         /**
  426.          * This is only overridden as part of a HACK solution to a bug in WP 3.0 not allowing suppression of the duplicate email check.
  427.          *
  428.          * What it does: Replaces WP's get_user_by_email(). If during the user creation process (hackily determined by the plugin's instance)
  429.          * AND the email has not exceeded the account limit, then return false.  wp_insert_user() calls this function simply to check if the
  430.          * email is already associated with an account.  So in that instance, if we know that's where the request is originating and that the
  431.          * email in question is allowed to have multiple accounts, then trick the check into thinking the email isn't in use so that an error
  432.          * isn't generated.
  433.          *
  434.          * @since 2.0
  435.          *
  436.          * @param string $email User email
  437.          * @return string User associated with the email
  438.          */
  439.         if ( !function_exists( 'get_user_by_email' ) ) {
  440.                 function get_user_by_email( $email ) {
  441.                         if ( $GLOBALS['c2c_allow_multiple_accounts']->during_user_creation && !$GLOBALS['c2c_allow_multiple_accounts']->has_exceeded_limit( $email ) )
  442.                                 return false;
  443.                         return get_user_by('email', $email);
  444.                 }
  445.         }
  446.  
  447.         /**
  448.          * *******************
  449.          * DEPRECATED FUNCTIONS
  450.          * *******************
  451.          */
  452.         if ( !function_exists( 'count_multiple_accounts' ) ) {
  453.                 function count_multiple_accounts( $email ) { return c2c_count_multiple_accounts( $email ); }
  454.         }
  455.         if ( !function_exists( 'get_users_by_email' ) ) {
  456.                 function get_users_by_email( $email ) { return c2c_get_users_by_email( $email ); }
  457.         }
  458.         if ( !function_exists( 'has_multiple_accounts' ) ) {
  459.                 function has_multiple_accounts( $email ) { return c2c_has_multiple_accounts( $email ); }
  460.         }
  461.  
  462. /* WP function */
  463. /*
  464.  * Patch to fix "Fatal error: Call to undefined function user_row() in .../wp-content/plugins/allow-multiple-accounts/allow-multiple-accounts.php on line 220
  465.  * 2010-03-09 J P Tarrier
  466.  */
  467.         function user_row( $user_object, $style = '' ) {
  468.                 if ( !(is_object($user_object) && is_a($user_object, 'WP_User')) )
  469.                         $user_object = new WP_User( (int) $user_object );
  470.                         $email = $user_object->user_email;
  471.                         $url = $user_object->user_url;
  472.                         $short_url = str_replace('http://', '', $url);
  473.                         $short_url = str_replace('www.', '', $short_url);
  474.                 if ('/' == substr($short_url, -1))
  475.                         $short_url = substr($short_url, 0, -1);
  476.                 if (strlen($short_url) > 35)
  477.                         $short_url =  substr($short_url, 0, 32).'...';
  478.                 $numposts = get_usernumposts($user_object->ID);
  479.                 $r = "<tr id='user-$user_object->ID'$style>
  480.                    <td><input type='checkbox' name='users[]' id='user_{$user_object->ID}' value='{$user_object->ID}' /> <label for='user_{$user_object->ID}'>{$user_object->ID}</label></td>
  481.                    <td><label for='user_{$user_object->ID}'><strong>$user_object->user_login</strong></label></td>
  482.                    <td><label for='user_{$user_object->ID}'>$user_object->first_name $user_object->last_name</label></td>
  483.                    <td><a href='mailto:$email' title='" . sprintf(__('e-mail: %s'), $email) . "'>$email</a></td>
  484.                    <td><a href='$url' title='website: $url'>$short_url</a></td>";
  485.                 $r .= "\n\t\t<td align='center'>";
  486.            if ($numposts > 0) {
  487.                    $r .= "<a href='edit.php?author=$user_object->ID' title='" . __('View posts by this author') . "' class='edit'>";
  488.                    $r .= sprintf(__('View %1$s %2$s'), $numposts, __ngettext('post', 'posts', $numposts));
  489.            }
  490.            $r .= "</td>\n\t\t<td>";
  491.            $edit_link = add_query_arg('wp_http_referer', wp_specialchars(urlencode(stripslashes($_SERVER['REQUEST_URI']))), "user-edit.php?user_id=$user_object->ID");
  492.            if ( current_user_can('edit_user', $user_object->ID) )
  493.                    $r .= "<a href='$edit_link' class='edit'>".__('Edit')."</a>";
  494.            $r .= "</td>\n\t</tr>";
  495.            return $r;
  496.         }
  497. ?>
clone this paste RAW Paste Data