कस्टम मेटा फ़ील्ड द्वारा उपयोगकर्ताओं को व्यवस्थापक उपयोगकर्ताओं के पेज पर फ़िल्टर कैसे करें?


9

समस्या

WP उपयोगकर्ताओं की सूची को फ़िल्टर करने के लिए उपयोग करने से पहले मेरे क्वेरी चर के मूल्य को हटाता हुआ प्रतीत होता है।

मेरा कोड

यह फ़ंक्शन मेरे उपयोगकर्ता तालिका में एक कस्टम कॉलम जोड़ता है /wp-admin/users.php:

function add_course_section_to_user_meta( $columns ) {
    $columns['course_section'] = 'Section';
    return $columns;
}
add_filter( 'manage_users_columns', 'add_course_section_to_user_meta' );

यह फ़ंक्शन WP बताता है कि कॉलम में मान कैसे भरें:

function manage_users_course_section( $val, $col, $uid ) {
    if ( 'course_section' === $col )
        return get_the_author_meta( 'course_section', $uid );
}
add_filter( 'manage_users_custom_column', 'manage_users_course_section' );

यह Filterउपयोगकर्ता तालिका के ऊपर एक ड्रॉपडाउन और बटन जोड़ता है :

function add_course_section_filter() {
    echo '<select name="course_section" style="float:none;">';
    echo '<option value="">Course Section...</option>';
    for ( $i = 1; $i <= 3; ++$i ) {
        if ( $i == $_GET[ 'course_section' ] ) {
            echo '<option value="'.$i.'" selected="selected">Section '.$i.'</option>';
        } else {
            echo '<option value="'.$i.'">Section '.$i.'</option>';
        }
    }
    echo '<input id="post-query-submit" type="submit" class="button" value="Filter" name="">';
}
add_action( 'restrict_manage_users', 'add_course_section_filter' );

यह फ़ंक्शन मेरे जोड़ने के लिए उपयोगकर्ता क्वेरी को बदल देता है meta_query:

function filter_users_by_course_section( $query ) {
    global $pagenow;

    if ( is_admin() && 
         'users.php' == $pagenow && 
         isset( $_GET[ 'course_section' ] ) && 
         !empty( $_GET[ 'course_section' ] ) 
       ) {
        $section = $_GET[ 'course_section' ];
        $meta_query = array(
            array(
                'key'   => 'course_section',
                'value' => $section
            )
        );
        $query->set( 'meta_key', 'course_section' );
        $query->set( 'meta_query', $meta_query );
    }
}
add_filter( 'pre_get_users', 'filter_users_by_course_section' );

अन्य सूचना

यह मेरे ड्रॉपडाउन को सही ढंग से बनाता है। जब मैं एक कोर्स सेक्शन का चयन करता हूं और Filterपेज रिफ्रेश पर क्लिक करता हूं और course_sectionURL में दिखाता हूं , लेकिन इसका कोई मूल्य नहीं है। यदि मैं HTTP अनुरोधों की जाँच करता हूँ, तो यह दिखाता है कि यह सही परिवर्तनीय मूल्य के साथ प्रस्तुत किया गया है, लेकिन फिर 302 Redirectमेरे द्वारा चुने गए मूल्य को हटा दिया गया है।

अगर मैं course_sectionवेरिएबल को सीधे URL में टाइप करके सबमिट करता हूं , तो फिल्टर उम्मीद के मुताबिक काम करता है।

मेरा कोड डेव कोर्ट के इस कोड पर आधारित है ।

मैंने इस कोड का उपयोग करके अपने क्वेरी संस्करण को श्वेत सूची में लाने की कोशिश की, लेकिन कोई भाग्य नहीं:

function add_course_section_query_var( $qvars ) {
    $qvars[] = 'course_section';
    return $qvars;
}
add_filter( 'query_vars', 'add_course_section_query_var' );

मैं WP 4.4 का उपयोग कर रहा हूं। कोई भी विचार क्यों मेरा फ़िल्टर काम नहीं कर रहा है?


FYI करें, मैंने WP Trac साइट पर एक टिकट जोड़ा है जो डेवलपर्स को नीचे वर्णित किसी भी हुप्स के माध्यम से कूदने से रोक देगा।
मैराफैटिक

जवाबों:


6

UPDATE 2018-06-28

जबकि नीचे दिया गया कोड अधिकतर ठीक काम करता है, यहां WP> = 4.6.0 (PHP 7 का उपयोग करके) कोड के लिए फिर से लिखना है:

function add_course_section_filter( $which ) {

    // create sprintf templates for <select> and <option>s
    $st = '<select name="course_section_%s" style="float:none;"><option value="">%s</option>%s</select>';
    $ot = '<option value="%s" %s>Section %s</option>';

    // determine which filter button was clicked, if any and set section
    $button = key( array_filter( $_GET, function($v) { return __( 'Filter' ) === $v; } ) );
    $section = $_GET[ 'course_section_' . $button ] ?? -1;

    // generate <option> and <select> code
    $options = implode( '', array_map( function($i) use ( $ot, $section ) {
        return sprintf( $ot, $i, selected( $i, $section, false ), $i );
    }, range( 1, 3 ) ));
    $select = sprintf( $st, $which, __( 'Course Section...' ), $options );

    // output <select> and submit button
    echo $select;
    submit_button(__( 'Filter' ), null, $which, false);
}
add_action('restrict_manage_users', 'add_course_section_filter');

function filter_users_by_course_section($query)
{
    global $pagenow;
    if (is_admin() && 'users.php' == $pagenow) {
        $button = key( array_filter( $_GET, function($v) { return __( 'Filter' ) === $v; } ) );
        if ($section = $_GET[ 'course_section_' . $button ]) {
            $meta_query = [['key' => 'courses','value' => $section, 'compare' => 'LIKE']];
            $query->set('meta_key', 'courses');
            $query->set('meta_query', $meta_query);
        }
    }
}
add_filter('pre_get_users', 'filter_users_by_course_section');

मैंने @birgire और @cale_b से कई विचारों को शामिल किया, जो नीचे दिए गए समाधानों को भी पढ़ने लायक हैं। विशेष रूप से, मैं:

  1. उस $whichचर का उपयोग किया जो इसमें जोड़ा गया थाv4.6.0
  2. अनुवाद योग्य तार का उपयोग करके i18n के लिए सबसे अच्छा अभ्यास किया, उदा __( 'Filter' )
  3. के लिए आदान-प्रदान किया छोरों (अधिक फैशनेबल?) array_map(), array_filter()औरrange()
  4. sprintf()मार्कअप टेम्प्लेट उत्पन्न करने के लिए उपयोग किया जाता है
  5. के बजाय वर्ग कोष्ठक सरणी संकेतन का उपयोग किया array()

अंत में, मैंने अपने पहले के समाधानों में एक बग की खोज की। वे समाधान हमेशा <select>BOTTOM पर शीर्ष का पक्ष लेते हैं <select>। इसलिए यदि आपने शीर्ष ड्रॉपडाउन से फ़िल्टर विकल्प का चयन किया है, और उसके बाद नीचे ड्रॉपडाउन में से एक का चयन करें, तो फ़िल्टर अभी भी केवल उसी का उपयोग करेगा जो ऊपर था (यदि यह रिक्त नहीं है)। यह नया संस्करण उस बग को ठीक करता है।

UPDATE 2018-02-14

WP 4.6.0 के बाद से इस मुद्दे को पैच किया गया है और आधिकारिक डॉक्स में बदलावों को प्रलेखित किया गया है । नीचे समाधान अभी भी काम करता है, हालांकि।

समस्या का क्या कारण है (WP <4.6.0)

समस्या यह थी कि restrict_manage_usersकार्रवाई को दो बार कहा जाता है: एक बार उपयोगकर्ता तालिका प्राप्त करें, और एक बार इसे बंद कर दें। इसका मतलब है कि TWO selectड्रॉपडाउन एक ही नाम से बनाए जाते हैं । जब Filterबटन पर क्लिक किया जाता है, तो जो भी मूल्य दूसरे selectतत्व में होता है (यानी एक तालिका को कम करता है) पहले एक में मान को ओवरराइड करता है, अर्थात एक तालिका को हटाता है।

यदि आप WP स्रोत में गोता लगाना चाहते हैं, तो restrict_manage_usersकार्रवाई को भीतर से ट्रिगर किया जाता है WP_Users_List_Table::extra_tablenav($which), जो कि फ़ंक्शन है जो उपयोगकर्ता की भूमिका को बदलने के लिए देशी ड्रॉपडाउन बनाता है। उस फ़ंक्शन में $whichवैरिएबल की मदद होती है जो यह बताता है कि क्या यह selectफॉर्म के ऊपर या नीचे बना है, और यह दो ड्रॉपडाउन को अलग-अलग nameविशेषताओं को देने की अनुमति देता है । दुर्भाग्य से, $whichचर restrict_manage_usersकार्रवाई के लिए पारित नहीं होता है , इसलिए हमें अपने स्वयं के कस्टम तत्वों को अलग करने के लिए एक और तरीका आना चाहिए।

ऐसा करने का एक तरीका, जैसा कि @Leaea सुझाव देता है , Filterक्लिक को पकड़ने और दो ड्रॉपडाउन के मूल्यों को सिंक करने के लिए कुछ जावास्क्रिप्ट जोड़ना होगा । मैंने एक PHP- एकमात्र समाधान चुना, जिसका मैं अब वर्णन करूंगा।

इसे कैसे जोड़ेंगे

आप HTML इनपुट को मूल्यों की सरणियों में बदलने की क्षमता का लाभ उठा सकते हैं, और फिर किसी भी अपरिभाषित मूल्यों से छुटकारा पाने के लिए सरणी को फ़िल्टर कर सकते हैं। यहाँ कोड है:

    function add_course_section_filter() {
        if ( isset( $_GET[ 'course_section' ]) ) {
            $section = $_GET[ 'course_section' ];
            $section = !empty( $section[ 0 ] ) ? $section[ 0 ] : $section[ 1 ];
        } else {
            $section = -1;
        }
        echo ' <select name="course_section[]" style="float:none;"><option value="">Course Section...</option>';
        for ( $i = 1; $i <= 3; ++$i ) {
            $selected = $i == $section ? ' selected="selected"' : '';
            echo '<option value="' . $i . '"' . $selected . '>Section ' . $i . '</option>';
        }
        echo '</select>';
        echo '<input type="submit" class="button" value="Filter">';
    }
    add_action( 'restrict_manage_users', 'add_course_section_filter' );

    function filter_users_by_course_section( $query ) {
        global $pagenow;

        if ( is_admin() && 
             'users.php' == $pagenow && 
             isset( $_GET[ 'course_section' ] ) && 
             is_array( $_GET[ 'course_section' ] )
            ) {
            $section = $_GET[ 'course_section' ];
            $section = !empty( $section[ 0 ] ) ? $section[ 0 ] : $section[ 1 ];
            $meta_query = array(
                array(
                    'key' => 'course_section',
                    'value' => $section
                )
            );
            $query->set( 'meta_key', 'course_section' );
            $query->set( 'meta_query', $meta_query );
        }
    }
    add_filter( 'pre_get_users', 'filter_users_by_course_section' );

बोनस: PHP 7 रिफ्लेक्टर

जब से आप PHP 7 सर्वर पर WP चला रहे हैं, तो मैं PHP 7 के बारे में उत्साहित हूं, यहाँ एक छोटा, कामुक संस्करण है जो अशक्त सहकर्मी ऑपरेटर?? का उपयोग कर रहा है :

function add_course_section_filter() {
    $section = $_GET[ 'course_section' ][ 0 ] ?? $_GET[ 'course_section' ][ 1 ] ?? -1;
    echo ' <select name="course_section[]" style="float:none;"><option value="">Course Section...</option>';
    for ( $i = 1; $i <= 3; ++$i ) {
        $selected = $i == $section ? ' selected="selected"' : '';
        echo '<option value="' . $i . '"' . $selected . '>Section ' . $i . '</option>';
    }
    echo '</select>';
    echo '<input type="submit" class="button" value="Filter">';
}
add_action( 'restrict_manage_users', 'add_course_section_filter' );

function filter_users_by_course_section( $query ) {
    global $pagenow;

    if ( is_admin() && 'users.php' == $pagenow) {
        $section = $_GET[ 'course_section' ][ 0 ] ?? $_GET[ 'course_section' ][ 1 ] ?? null;
        if ( null !== $section ) {
            $meta_query = array(
                array(
                    'key' => 'course_section',
                    'value' => $section
                )
            );
            $query->set( 'meta_key', 'course_section' );
            $query->set( 'meta_query', $meta_query );
        }
    }
}
add_filter( 'pre_get_users', 'filter_users_by_course_section' );

का आनंद लें!


तो क्या आपका समाधान 4.6.0 के बाद भी काम करता है? क्या वर्डप्रेस के नवीनतम संस्करण के साथ ऐसा करने का एक आसान तरीका है? मुझे ऐसा कोई गाइड नहीं मिल रहा है, जैसे इस साल
जेरेमी मुकल

1
@JeremyMuckel आपके प्रश्न का संक्षिप्त उत्तर है "हाँ।" मेरा पुराना हल अभी भी काम करता है। अब मैं इसे महीनों से नियमित रूप से उत्पादन में उपयोग कर रहा हूं और मेरी अधिकांश साइटें नवीनतम स्थिर WP संस्करण (वर्तमान में 4.9.6) में अपडेट हैं। कहा जा रहा है, मैंने एक अद्यतन समाधान प्रदान किया है जो नए पैच का उपयोग करता है और जो मेरे पिछले समाधान में एक सूक्ष्म बग को भी ठीक करता है।
मोरफटिक

यह मददगार था लेकिन "हाउ टू फिक्स इट" और "बोनस: पीएचपी 7 रिफैक्टर" के तहत आपका फॉर्म कोड </select>मुझे याद आ रहा है, मुझे यह काम करने के लिए भी मिला है जिसे मुझे <form method="get">चयन मेनू से पहले और </form>फ़िल्टर बटन के बाद रखना था ।
कोजोग

@cogdog लापता </select>टैग पर अच्छी पकड़ ! मैंने उन्हें जोड़ा। अजीब बात है कि आपको इसे लपेटने की आवश्यकता थी <form>क्योंकि यह पूरा पृष्ठ एक बड़े रूप में लिपटा हुआ है, और यह कोड इसके बीच में इंजेक्ट हो जाता है। खुशी है कि आप इसे काम कर रहे हैं, हालांकि। :)
मोर्थैटिक

4

कोर में, नीचे के इनपुट नामों को उदाहरण संख्या, जैसे new_role(शीर्ष) और new_role2(नीचे) के साथ चिह्नित किया जाता है । यहां समान नामकरण सम्मेलन के लिए दो दृष्टिकोण दिए गए हैं, जिनके नाम course_section1(शीर्ष) और course_section2(नीचे) हैं:

दृष्टिकोण # १

चूँकि $whichचर ( ऊपर , नीचे ) restrict_manage_usersहुक के पास नहीं जाता है , हम उस हुक का अपना संस्करण बनाकर उसे प्राप्त कर सकते हैं:

चलिए उस एक्शन हुक को बनाते हैं जिसमें wpse_restrict_manage_usersएक $whichवैरिएबल का उपयोग होता है :

add_action( 'restrict_manage_users', function() 
{
    static $instance = 0;   
    do_action( 'wpse_restrict_manage_users', 1 === ++$instance ? 'top' : 'bottom'  );

} );

फिर हम इसे हुक कर सकते हैं:

add_action( 'wpse_restrict_manage_users', function( $which )
{
    $name = 'top' === $which ? 'course_section1' : 'course_section2';

    // your stuff here
} );

जहां अब हमारे पास $nameके रूप में course_section1पर शीर्ष और course_section2पर नीचे

दृष्टिकोण # 2

आइए restrict_manage_users, प्रत्येक उदाहरण के लिए एक अलग नाम के साथ, ड्रॉपडाउन प्रदर्शित करने के लिए हुक करें :

function add_course_section_filter() 
{
    static $instance= 0;    

    // Dropdown options         
    $options = '';
    foreach( range( 1, 3 ) as $rng )
    {
        $options = sprintf( 
            '<option value="%1$d" %2$s>Section %1$d</option>',
            $rng,
            selected( $rng, get_selected_course_section(), 0 )
        );
    }

    // Display dropdown with a different name for each instance
    printf( 
        '<select name="%s" style="float:none;"><option value="0">%s</option>%s</select>', 
        'course_section' . ++$instance,
        __( 'Course Section...' ),
        $options 
    );


    // Button
    printf (
        '<input id="post-query-submit" type="submit" class="button" value="%s" name="">',
        __( 'Filter' )
    );
}
add_action( 'restrict_manage_users', 'add_course_section_filter' );

जहाँ हमने कोर फंक्शन selected()और हेल्पर फंक्शन का इस्तेमाल किया है :

/**
 * Get the selected course section 
 * @return int $course_section
 */
function get_selected_course_section()
{
    foreach( range( 1, 2) as $rng )
        $course_section = ! empty( $_GET[ 'course_section' . $rng ] )
            ? $_GET[ 'course_section' . $rng ]
            : -1; // default

    return (int) $course_section;
}

तब हम इसका उपयोग तब भी कर सकते हैं जब हम pre_get_usersएक्शन कॉलबैक में चयनित कोर्स सेक्शन की जांच करते हैं ।


यह एक आकर्षक दृष्टिकोण है। मैंने कभी भी staticकुंजी शब्द का उपयोग इस तरह से नहीं किया है (केवल कक्षाओं के भीतर)। करता है $instanceएक वैश्विक चर हो जाते हैं जब आप ऐसा करते हैं? क्या आपको चर नाम टकराव के बारे में चिंता करने की ज़रूरत है? मुझे एक नया एक्शन बनाने की तकनीक भी पसंद है, जो गुल्लक किसी मौजूदा पर वापस आती है। धन्यवाद!
मूरफैटिक

यह दृष्टिकोण कभी-कभी आसान हो सकता है और इसका उपयोग शोर्ट (गैलरी, प्लेलिस्ट, ऑडियो) उदाहरणों की गणना करने के लिए कोर में किया जाता है। यहां स्थिर वैरिएबल स्कोप वैश्विक वैरिएबल स्कोप के साथ गड़बड़ नहीं करेगा। स्थिर चर का मान उन फ़ंक्शन कॉलों के बीच संरक्षित किया जाएगा, जो स्थानीय चर के मामले में नहीं है। मैंने इस अच्छे ट्यूटोरियल को खोजा और पाया जिसमें अधिक विवरण हैं। @ बोरफिक
बीरगीर

4

मैंने वर्डप्रेस 4.4 और वर्डप्रेस 4.3.1 दोनों में आपके कोड का परीक्षण किया। 4.4 संस्करण के साथ, मैं आपके समान ही समस्या का सामना करता हूं। हालाँकि, आपका कोड 4.3.1 संस्करण में सही ढंग से काम करता है!

मुझे लगता है कि यह एक Wordpress बग है। मुझे नहीं पता कि यह अभी तक रिपोर्ट किया गया है। मुझे लगता है कि बग के पीछे का कारण यह हो सकता है कि सबमिट बटन दो बार क्वेरी संस्करण भेज रहा है। यदि आप क्वेरी संस्करण को देखते हैं, तो आप देखेंगे कि course_section दो बार सूचीबद्ध है, एक बार सही मान के साथ और एक बार खाली।

संपादित करें: यह जावास्क्रिप्ट समाधान है

बस इसे अपनी थीम के functions.php फ़ाइल में जोड़ें और अपने इनपुट फ़ील्ड के नाम में NAME_OF_YOUR_INPUT_FIELD बदलें! चूंकि वर्डप्रेस व्यवस्थापक पक्ष पर स्वचालित रूप से jQuery लोड करता है, इसलिए आपको किसी भी स्क्रिप्ट को संलग्न करने की आवश्यकता नहीं है। कोड का यह स्निपेट केवल ड्रॉपडाउन इनपुट के लिए एक परिवर्तन श्रोता जोड़ता है और फिर उसी मूल्य से मिलान करने के लिए स्वचालित रूप से अन्य ड्रॉपडाउन को अपडेट करता है। यहाँ और अधिक स्पष्टीकरण।

add_action( 'in_admin_footer', function() {
?>
<script type="text/javascript">
    var el = jQuery("[name='NAME_OF_YOUR_INPUT_FIELD']");
    el.change(function() {
        el.val(jQuery(this).val());
    });
</script>
<?php
} );

उम्मीद है की यह मदद करेगा!


धन्यवाद Linnea। हां, मुझे एक ही बात पता Filterचली , कि जब आप इसे क्लिक करते हैं, तो यह सही मान जमा करता है, लेकिन फिर पृष्ठ पर वापस भेज देता है, इस बार मान को अलग कर देता है। मेरा अनुमान है कि यह यादृच्छिक, संभावित दुर्भावनापूर्ण, मूल्यों को प्रस्तुत किए जाने से रोकने के लिए किसी प्रकार की सुरक्षा "सुविधा" है, लेकिन मुझे नहीं पता कि इसके आसपास कैसे काम किया जाए। आह।
मोर्थैटिक

ओह! मुझे पता चला कि var दो बार क्यों दिखाता है। क्योंकि ABOVE और BELOW दोनों उपयोगकर्ताओं की तालिका में एक गिरावट है और दोनों की nameविशेषता समान है। यदि मैं फ़िल्टरिंग करने के लिए तालिका को ड्रॉप डाउन का उपयोग करता हूं, तो यह अपेक्षित रूप से काम करता है। चूँकि वह क्षेत्र इसके ऊपर वाले हिस्से के बाद आता है, इसलिए यह शून्य मान पहले वाले को ओवरराइड करता है।
हम्मम

अच्छा खोजो! मैं यह पता लगाने की कोशिश कर रहा था कि डुप्लिकेट कहां से आ रहा था। मुझे लगता है कि शायद थोड़ा जावास्क्रिप्ट इसे ठीक कर सकता है। क्या उसने फॉर्म जमा करने से पहले अन्य ड्रॉप को उसी मान के रूप में सेट किया है।
लिनिया हक्सफ़ोर्ड

1

यह एक अलग जावास्क्रिप्ट समाधान है जो कुछ लोगों के लिए मददगार हो सकता है। मेरे मामले में मैंने बस 2 डी (नीचे) चयन सूची को पूरी तरह से हटा दिया। मुझे लगता है कि मैं वैसे भी नीचे इनपुट का उपयोग कभी नहीं ...

add_action( 'in_admin_footer', function() {
    ?>
    <script type="text/javascript">
        jQuery(".tablenav.bottom select[name='course_section']").remove();
        jQuery(".tablenav.bottom input#post-query-submit").remove();
    </script>
    <?php
} );

1

गैर-जावास्क्रिप्ट समाधान

चयन को एक नाम दें जो "सरणी-शैली" है, जैसे:

echo '<select name="course_section[]" style="float:none;">';

फिर बीओटीएच मापदंडों को पारित किया जाता है (तालिका के शीर्ष और बॉटम से), और अब एक ज्ञात सरणी प्रारूप में।

फिर, pre_get_usersफ़ंक्शन में मान का उपयोग इस तरह किया जा सकता है :

function filter_users_by_course_section( $query ) {
    global $pagenow;

    // if not on users page in admin, get out
    if ( ! is_admin() || 'users.php' != $pagenow ) {
        return;
    } 

    // if no section selected, get out
    if ( empty( $_GET['course_section'] ) ) {
        return;
    }

    // course_section is known to be set now, so load it
    $section = $_GET['course_section'];

    // the value is an array, and one of the two select boxes was likely
    // not set to anything, so use array_filter to eliminate empty elements
    $section = array_filter( $section );

    // the value is still an array, so get the first value
    $section = reset( $section );

    // now the value is a single value, such as 1
    $meta_query = array(
        array(
            'key' => 'course_section',
            'value' => $section
        )
    );

    $query->set( 'meta_key', 'course_section' );
    $query->set( 'meta_query', $meta_query );
}

0

एक और समाधान

आप अपने फ़िल्टर चयन बॉक्स को अलग फ़ाइल में रख सकते हैं जैसे user_list_filter.php

और require_once 'user_list_filter.php'अपने एक्शन कॉलबैक फ़ंक्शन में उपयोग करें

user_list_filter.php फ़ाइल:

<select name="course_section" style="float:none;">
    <option value="">Course Section...</option>
    <?php for ( $i = 1; $i <= 3; ++$i ) {
        if ( $i == $_GET[ 'course_section' ] ) { ?>
        <option value="<?=$i?>" selected="selected">Section <?=$i?></option>
        <?php } else { ?>
        <option value="<?=$i?>">Section <?=$i?></option>
        <?php }
     }?>
</select>
<input id="post-query-submit" type="submit" class="button" value="Filter" name="">

और आपके एक्शन कॉलबैक में:

function add_course_section_filter() {
    require_once 'user_list_filter.php';
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.