varnish statistics tab
[varnish-caching-wordpress-plugin.git] / vcaching.php
blob:a/vcaching.php -> blob:b/vcaching.php
--- a/vcaching.php
+++ b/vcaching.php
@@ -3,7 +3,7 @@
 Plugin Name: VCaching
 Plugin URI: http://wordpress.org/extend/plugins/vcaching/
 Description: WordPress Varnish Cache integration.
-Version: 1.3
+Version: 1.4
 Author: Razvan Stanga
 Author URI: http://git.razvi.ro/
 License: http://www.apache.org/licenses/LICENSE-2.0
@@ -22,6 +22,7 @@
     protected $varnishHost = null;
     protected $dynamicHost = null;
     protected $ipsToHosts = array();
+    protected $statsJsons = array();
     protected $purgeKey = null;
     protected $getParam = 'purge_varnish_cache';
     protected $postTypes = array('page', 'post');
@@ -29,209 +30,6 @@
     protected $customFields = array();
     protected $noticeMessage = '';
     protected $debug = 0;
-    protected $varnistStats = array(
-        "client_conn" => "Client connections accepted",
-        "client_drop" => "Connection dropped, no sess/wrk",
-        "client_req" => "Client requests received",
-        "cache_hit" => "Cache hits",
-        "cache_hitpass" => "Cache hits for pass",
-        "cache_miss" => "Cache misses",
-        "backend_conn" => "Backend conn. success",
-        "backend_unhealthy" => "Backend conn. not attempted",
-        "backend_busy" => "Backend conn. too many",
-        "backend_fail" => "Backend conn. failures",
-        "backend_reuse" => "Backend conn. reuses",
-        "backend_toolate" => "Backend conn. was closed",
-        "backend_recycle" => "Backend conn. recycles",
-        "backend_retry" => "Backend conn. retry",
-        "fetch_head" => "Fetch head",
-        "fetch_length" => "Fetch with Length",
-        "fetch_chunked" => "Fetch chunked",
-        "fetch_eof" => "Fetch EOF",
-        "fetch_bad" => "Fetch had bad headers",
-        "fetch_close" => "Fetch wanted close",
-        "fetch_oldhttp" => "Fetch pre HTTP/1.1 closed",
-        "fetch_zero" => "Fetch zero len",
-        "fetch_failed" => "Fetch failed",
-        "fetch_1xx" => "Fetch no body (1xx)",
-        "fetch_204" => "Fetch no body (204)",
-        "fetch_304" => "Fetch no body (304)",
-        "n_sess_mem" => "N struct sess_mem",
-        "n_sess" => "N struct sess",
-        "n_object" => "N struct object",
-        "n_vampireobject" => "N unresurrected objects",
-        "n_objectcore" => "N struct objectcore",
-        "n_objecthead" => "N struct objecthead",
-        "n_waitinglist" => "N struct waitinglist",
-        "n_vbc" => "N struct vbc",
-        "n_wrk" => "N worker threads",
-        "n_wrk_create" => "N worker threads created",
-        "n_wrk_failed" => "N worker threads not created",
-        "n_wrk_max" => "N worker threads limited",
-        "n_wrk_lqueue" => "work request queue length",
-        "n_wrk_queued" => "N queued work requests",
-        "n_wrk_drop" => "N dropped work requests",
-        "n_backend" => "N backends",
-        "n_expired" => "N expired objects",
-        "n_lru_nuked" => "N LRU nuked objects",
-        "n_lru_moved" => "N LRU moved objects",
-        "losthdr" => "HTTP header overflows",
-        "n_objsendfile" => "Objects sent with sendfile",
-        "n_objwrite" => "Objects sent with write",
-        "n_objoverflow" => "Objects overflowing workspace",
-        "s_sess" => "Total Sessions",
-        "s_req" => "Total Requests",
-        "s_pipe" => "Total pipe",
-        "s_pass" => "Total pass",
-        "s_fetch" => "Total fetch",
-        "s_hdrbytes" => "Total header bytes",
-        "s_bodybytes" => "Total body bytes",
-        "sess_closed" => "Session Closed",
-        "sess_pipeline" => "Session Pipeline",
-        "sess_readahead" => "Session Read Ahead",
-        "sess_linger" => "Session Linger",
-        "sess_herd" => "Session herd",
-        "shm_records" => "SHM records",
-        "shm_writes" => "SHM writes",
-        "shm_flushes" => "SHM flushes due to overflow",
-        "shm_cont" => "SHM MTX contention",
-        "shm_cycles" => "SHM cycles through buffer",
-        "sms_nreq" => "SMS allocator requests",
-        "sms_nobj" => "SMS outstanding allocations",
-        "sms_nbytes" => "SMS outstanding bytes",
-        "sms_balloc" => "SMS bytes allocated",
-        "sms_bfree" => "SMS bytes freed",
-        "backend_req" => "Backend requests made",
-        "n_vcl" => "N vcl total",
-        "n_vcl_avail" => "N vcl available",
-        "n_vcl_discard" => "N vcl discarded",
-        "n_ban" => "N total active bans",
-        "n_ban_gone" => "N total gone bans",
-        "n_ban_add" => "N new bans added",
-        "n_ban_retire" => "N old bans deleted",
-        "n_ban_obj_test" => "N objects tested",
-        "n_ban_re_test" => "N regexps tested against",
-        "n_ban_dups" => "N duplicate bans removed",
-        "hcb_nolock" => "HCB Lookups without lock",
-        "hcb_lock" => "HCB Lookups with lock",
-        "hcb_insert" => "HCB Inserts",
-        "esi_errors" => "ESI parse errors (unlock)",
-        "esi_warnings" => "ESI parse warnings (unlock)",
-        "accept_fail" => "Accept failures",
-        "client_drop_late" => "Connection dropped late",
-        "uptime" => "Client uptime",
-        "dir_dns_lookups" => "DNS director lookups",
-        "dir_dns_failed" => "DNS director failed lookups",
-        "dir_dns_hit" => "DNS director cached lookups hit",
-        "dir_dns_cache_full" => "DNS director full dnscache",
-        "vmods" => "Loaded VMODs",
-        "n_gzip" => "Gzip operations",
-        "n_gunzip" => "Gunzip operations",
-        "sess_pipe_overflow" => "Dropped sessions due to session pipe overflow",
-        "LCK.sms.creat" => "Created locks",
-        "LCK.sms.destroy" => "Destroyed locks",
-        "LCK.sms.locks" => "Lock Operations",
-        "LCK.sms.colls" => "Collisions",
-        "LCK.smp.creat" => "Created locks",
-        "LCK.smp.destroy" => "Destroyed locks",
-        "LCK.smp.locks" => "Lock Operations",
-        "LCK.smp.colls" => "Collisions",
-        "LCK.sma.creat" => "Created locks",
-        "LCK.sma.destroy" => "Destroyed locks",
-        "LCK.sma.locks" => "Lock Operations",
-        "LCK.sma.colls" => "Collisions",
-        "LCK.smf.creat" => "Created locks",
-        "LCK.smf.destroy" => "Destroyed locks",
-        "LCK.smf.locks" => "Lock Operations",
-        "LCK.smf.colls" => "Collisions",
-        "LCK.hsl.creat" => "Created locks",
-        "LCK.hsl.destroy" => "Destroyed locks",
-        "LCK.hsl.locks" => "Lock Operations",
-        "LCK.hsl.colls" => "Collisions",
-        "LCK.hcb.creat" => "Created locks",
-        "LCK.hcb.destroy" => "Destroyed locks",
-        "LCK.hcb.locks" => "Lock Operations",
-        "LCK.hcb.colls" => "Collisions",
-        "LCK.hcl.creat" => "Created locks",
-        "LCK.hcl.destroy" => "Destroyed locks",
-        "LCK.hcl.locks" => "Lock Operations",
-        "LCK.hcl.colls" => "Collisions",
-        "LCK.vcl.creat" => "Created locks",
-        "LCK.vcl.destroy" => "Destroyed locks",
-        "LCK.vcl.locks" => "Lock Operations",
-        "LCK.vcl.colls" => "Collisions",
-        "LCK.stat.creat" => "Created locks",
-        "LCK.stat.destroy" => "Destroyed locks",
-        "LCK.stat.locks" => "Lock Operations",
-        "LCK.stat.colls" => "Collisions",
-        "LCK.sessmem.creat" => "Created locks",
-        "LCK.sessmem.destroy" => "Destroyed locks",
-        "LCK.sessmem.locks" => "Lock Operations",
-        "LCK.sessmem.colls" => "Collisions",
-        "LCK.wstat.creat" => "Created locks",
-        "LCK.wstat.destroy" => "Destroyed locks",
-        "LCK.wstat.locks" => "Lock Operations",
-        "LCK.wstat.colls" => "Collisions",
-        "LCK.herder.creat" => "Created locks",
-        "LCK.herder.destroy" => "Destroyed locks",
-        "LCK.herder.locks" => "Lock Operations",
-        "LCK.herder.colls" => "Collisions",
-        "LCK.wq.creat" => "Created locks",
-        "LCK.wq.destroy" => "Destroyed locks",
-        "LCK.wq.locks" => "Lock Operations",
-        "LCK.wq.colls" => "Collisions",
-        "LCK.objhdr.creat" => "Created locks",
-        "LCK.objhdr.destroy" => "Destroyed locks",
-        "LCK.objhdr.locks" => "Lock Operations",
-        "LCK.objhdr.colls" => "Collisions",
-        "LCK.exp.creat" => "Created locks",
-        "LCK.exp.destroy" => "Destroyed locks",
-        "LCK.exp.locks" => "Lock Operations",
-        "LCK.exp.colls" => "Collisions",
-        "LCK.lru.creat" => "Created locks",
-        "LCK.lru.destroy" => "Destroyed locks",
-        "LCK.lru.locks" => "Lock Operations",
-        "LCK.lru.colls" => "Collisions",
-        "LCK.cli.creat" => "Created locks",
-        "LCK.cli.destroy" => "Destroyed locks",
-        "LCK.cli.locks" => "Lock Operations",
-        "LCK.cli.colls" => "Collisions",
-        "LCK.ban.creat" => "Created locks",
-        "LCK.ban.destroy" => "Destroyed locks",
-        "LCK.ban.locks" => "Lock Operations",
-        "LCK.ban.colls" => "Collisions",
-        "LCK.vbp.creat" => "Created locks",
-        "LCK.vbp.destroy" => "Destroyed locks",
-        "LCK.vbp.locks" => "Lock Operations",
-        "LCK.vbp.colls" => "Collisions",
-        "LCK.vbe.creat" => "Created locks",
-        "LCK.vbe.destroy" => "Destroyed locks",
-        "LCK.vbe.locks" => "Lock Operations",
-        "LCK.vbe.colls" => "Collisions",
-        "LCK.backend.creat" => "Created locks",
-        "LCK.backend.destroy" => "Destroyed locks",
-        "LCK.backend.locks" => "Lock Operations",
-        "LCK.backend.colls" => "Collisions",
-        "SMF.s0.c_req" => "Allocator requests",
-        "SMF.s0.c_fail" => "Allocator failures",
-        "SMF.s0.c_bytes" => "Bytes allocated",
-        "SMF.s0.c_freed" => "Bytes freed",
-        "SMF.s0.g_alloc" => "Allocations outstanding",
-        "SMF.s0.g_bytes" => "Bytes outstanding",
-        "SMF.s0.g_space" => "Bytes available",
-        "SMF.s0.g_smf" => "N struct smf",
-        "SMF.s0.g_smf_frag" => "N small free smf",
-        "SMF.s0.g_smf_large" => "N large free smf",
-        "SMA.Transient.c_req" => "Allocator requests",
-        "SMA.Transient.c_fail" => "Allocator failures",
-        "SMA.Transient.c_bytes" => "Bytes allocated",
-        "SMA.Transient.c_freed" => "Bytes freed",
-        "SMA.Transient.g_alloc" => "Allocations outstanding",
-        "SMA.Transient.g_bytes" => "Bytes outstanding",
-        "SMA.Transient.g_space" => "Bytes available",
-        "VBE.default(192.168.0.2,,80).vcls" => "VCL references",
-        "VBE.default(192.168.0.2,,80).happy" => "Happy health probes",
-    );
 
     public function __construct()
     {
@@ -270,6 +68,10 @@
         // send headers to varnish
         add_action('send_headers', array($this, 'send_headers'), 1000000);
 
+        // logged in cookie
+        add_action('wp_login', array($this, 'wp_login'), 1000000);
+        add_action('wp_logout', array($this, 'wp_logout'), 1000000);
+
         // register events to purge post
         foreach ($this->getRegisterEvents() as $event) {
             add_action($event, array($this, 'purge_post'), 10, 2);
@@ -343,12 +145,15 @@
         $this->varnishIp = get_option($this->prefix . 'ips');
         $this->varnishHost = get_option($this->prefix . 'hosts');
         $this->dynamicHost = get_option($this->prefix . 'dynamic_host');
+        $this->statsJsons = get_option($this->prefix . 'stats_json_file');
         $varnishIp = explode(',', $this->varnishIp);
         $varnishHost = explode(',', $this->varnishHost);
+        $statsJsons = explode(',', $this->statsJsons);
         foreach ($varnishIp as $key => $ip) {
             $this->ipsToHosts[] = array(
                 'ip' => $ip,
-                'host' => $this->dynamicHost ? $_SERVER['HTTP_HOST'] : $varnishHost[$key]
+                'host' => $this->dynamicHost ? $_SERVER['HTTP_HOST'] : $varnishHost[$key],
+                'statsJson' => isset($statsJsons[$key]) ? $statsJsons[$key] : null
             );
         }
     }
@@ -457,13 +262,13 @@
 
     public function purge_varnish_cache_all_adminbar($admin_bar)
     {
-        $admin_bar->add_menu( array(
+        $admin_bar->add_menu(array(
             'id'    => 'purge-all-varnish-cache',
             'title' => __('Purge ALL Varnish Cache', $this->plugin),
             'href'  => wp_nonce_url(add_query_arg($this->getParam, 1), $this->plugin),
             'meta'  => array(
                 'title' => __('Purge ALL Varnish Cache', $this->plugin),
-            ),
+            )
         ));
     }
 
@@ -645,7 +450,12 @@
         $enable = get_option($this->prefix . 'enable');
         if ($enable) {
             Header('X-VC-Enabled: true', true);
-            $ttl = get_option($this->prefix . 'ttl');
+            if (is_user_logged_in()) {
+                Header('X-VC-Cacheable: NO:User is logged in', true);
+                $ttl = 0;
+            } else {
+                $ttl = get_option($this->prefix . 'ttl');
+            }
             Header('X-VC-TTL: ' . $ttl, true);
             if ($debug = get_option($this->prefix . 'debug')) {
                 Header('X-VC-Debug: true', true);
@@ -655,11 +465,24 @@
         }
     }
 
+    public function wp_login()
+    {
+        $cookie = get_option($this->prefix . 'cookie');
+        setcookie($cookie, 1, time()+3600*24*100, COOKIEPATH, COOKIE_DOMAIN, false, true);
+    }
+
+    public function wp_logout()
+    {
+        $cookie = get_option($this->prefix . 'cookie');
+        setcookie($cookie, null, time()-3600*24*100, COOKIEPATH, COOKIE_DOMAIN, false, true);
+    }
+
     public function admin_menu()
     {
         add_action('admin_menu', array($this, 'add_menu_item'));
         add_action('admin_init', array($this, 'options_page_fields'));
         add_action('admin_init', array($this, 'console_page_fields'));
+        add_action('admin_init', array($this, 'conf_page_fields'));
     }
 
     public function add_menu_item()
@@ -681,6 +504,7 @@
                 <a class="nav-tab <?php if($_GET['tab'] == 'console'): ?>nav-tab-active<?php endif; ?>" href="<?php echo admin_url() ?>index.php?page=<?=$this->plugin?>-plugin&amp;tab=console"><?=__('Console', $this->plugin)?></a>
             <?php endif; ?>
             <a class="nav-tab <?php if($_GET['tab'] == 'stats'): ?>nav-tab-active<?php endif; ?>" href="<?php echo admin_url() ?>index.php?page=<?=$this->plugin?>-plugin&amp;tab=stats"><?=__('Statistics', $this->plugin)?></a>
+            <a class="nav-tab <?php if($_GET['tab'] == 'conf'): ?>nav-tab-active<?php endif; ?>" href="<?php echo admin_url() ?>index.php?page=<?=$this->plugin?>-plugin&amp;tab=conf"><?=__('Varnish VCLs', $this->plugin)?></a>
         </h2>
 
         <?php if(!isset($_GET['tab']) || $_GET['tab'] == 'options'): ?>
@@ -691,6 +515,18 @@
                     submit_button();
                 ?>
             </form>
+            <script type="text/javascript">
+                function generateHash(length, bits, id) {
+                    bits = bits || 36;
+                    var outStr = "", newStr;
+                    while (outStr.length < length)
+                    {
+                        newStr = Math.random().toString(bits).slice(2);
+                        outStr += newStr.slice(0, Math.min(newStr.length, (length - outStr.length)));
+                    }
+                    jQuery('#' + id).val(outStr);
+                }
+            </script>
         <?php elseif($_GET['tab'] == 'console'): ?>
             <form method="post" action="index.php?page=<?=$this->plugin?>-plugin&amp;tab=console">
                 <?php
@@ -700,50 +536,92 @@
                 ?>
             </form>
         <?php elseif($_GET['tab'] == 'stats'): ?>
-            <?php
-                $error = null;
-                if (class_exists('VarnishStat')) {
-                    $vs = new VarnishStat;
-                    try {
-                        $stats = $vs->getSnapshot();
-                    } catch (VarnishException $e) {
-                        $error = $e->getMessage();
-                    }
-                } else {
-                    $error = __('Stats are available only with Varnish PECL extension installed. See <a href="https://pecl.php.net/package/varnish" target="_blank">https://pecl.php.net/package/varnish</a>.', $this->plugin);
-                }
-            ?>
             <h2><?= __('Statistics', $this->plugin) ?></h2>
-            <?php if($error == null): ?>
-            <table class="fixed">
-                <?php if(count($stats)): ?>
-                <thead>
-                    <tr>
-                        <td><strong><?= __('Key', $this->plugin) ?></strong></td>
-                        <td><strong><?= __('Value', $this->plugin) ?></strong></td>
-                        <td><strong><?= __('Description', $this->plugin) ?></strong></td>
-                    </tr>
-                </thead>
-                <tbody>
-                    <?php foreach ($stats as $key => $value): ?>
-                    <tr>
-                        <td><?=$key?></td>
-                        <td><?=$value?></td>
-                        <td><?=isset($this->varnistStats[$key]) ? __($this->varnistStats[$key], $this->plugin) : ''?></td>
-                    </tr>
+
+            <div class="wrap">
+                <?php if ($_GET['info'] == 1 || trim($this->statsJsons) == ""): ?>
+                    <div class="fade">
+                        <h4><?=__('Setup information', $this->plugin)?></h4>
+                        <?= __('<strong>Short story</strong><br />You must generate by cronjob the JSON stats file. The generated files must be copied on the backend servers in the wordpress root folder.', $this->plugin) ?>
+                        <br /><br />
+                        <?=sprintf(__('<strong>Long story</strong><br />On every Varnish Cache server setup a cronjob that generates the JSON stats file :<br /> %1$s /path/to/be/set/filename.json # every 3 minutes.', $this->plugin), '*/3 * * * *     root   /usr/bin/varnishstat -1j >')?>
+                        <br />
+                        <?= __('The generated files must be copied on the backend servers in the wordpress root folder.', $this->plugin) ?>
+                        <br />
+                        <?=__("Use a different filename for each Varnish Cache server. After this is done, fill in the relative path to the files in Statistics JSONs on the Settings tab.", $this->plugin)?>
+                        <br /><br />
+                        <?= __('Example 1 <br />If you have a single server, both Varnish Cache and the backend on it, use the folowing cronjob:', $this->plugin) ?>
+                        <br />
+                        <?=sprintf(__('%1$s /path/to/the/wordpress/root/varnishstat.json # every 3 minutes.', $this->plugin), '*/3 * * * *     root   /usr/bin/varnishstat -1j >')?>
+                        <br />
+                        <?=__("Then fill in the relative path to the files in Statistics JSONs on the Settings tab :", $this->plugin)?>
+                        <br />
+                        <input type="text" size="100" value="<?=__("/varnishstat.json", $this->plugin)?>" />
+
+                        <br /><br />
+                        <?=__("Example 2 <br />You have 2 Varnish Cache Servers, and 3 backend servers. Setup the cronjob :", $this->plugin)?>
+                        <br />
+                        <?=sprintf(__('VC Server 1 : %1$s # every 3 minutes.', $this->plugin), '*/3 * * * *     root   /usr/bin/varnishstat -1j > /root/varnishstat/server1_3389398cd359cfa443f85ca040da069a.json')?>
+                        <br />
+                        <?=sprintf(__('VC Server 2 : %1$s # every 3 minutes.', $this->plugin), '*/3 * * * *     root   /usr/bin/varnishstat -1j > /root/varnishstat/server2_3389398cd359cfa443f85ca040da069a.json')?>
+                        <br />
+                        <?=__("Copy the files on the backend servers in /path/to/wordpress/root/varnishstat/ folder. Then fill in the relative path to the files in Statistics JSONs on the Settings tab :", $this->plugin)?>
+                        <br />
+
+                        <input type="text" size="100" value="<?=__("/varnishstat/server1_3389398cd359cfa443f85ca040da069a.json,/varnishstat/server2_3389398cd359cfa443f85ca040da069a.json", $this->plugin)?>" />
+                    </div>
+                <?php endif; ?>
+
+                <?php if(trim($this->statsJsons)): ?>
+                    <h2 class="nav-tab-wrapper">
+                        <?php foreach ($this->ipsToHosts as $server => $ipToHost): ?>
+                            <a class="server nav-tab <?php if($server == 0) echo "nav-tab-active"; ?>" href="#" server="<?=$server?>"><?= sprintf(__('Server %1$s', $this->plugin), $ipToHost['ip'])?></a>
+                        <?php endforeach; ?>
+                    </h2>
+
+                    <?php foreach ($this->ipsToHosts as $server => $ipToHost): ?>
+                        <div id="server_<?=$server?>" class="servers" style="display:<?php if($server == 0) {echo 'block';} else {echo 'none';} ?>">
+                            <?= sprintf(__('Fetching stats for server %1$s', $this->plugin), $ipToHost['ip']) ?>
+                        </div>
+                        <script type="text/javascript">
+                            jQuery.getJSON("<?=$ipToHost['statsJson']?>", function(data) {
+                                var server = '#server_<?=$server?>';
+                                jQuery(server).html('');
+                                jQuery(server).append('<p><?= __('Stats generated on', $this->plugin) ?> ' + data.timestamp + '</p>');
+                                jQuery(server).append('<table class="wp-list-table widefat fixed striped server_<?=$server?>"><thead><tr><td class="manage-column"><strong><?= __('Description', $this->plugin) ?></strong></td><td class="manage-column"><strong><?= __('Value', $this->plugin) ?></strong></td><td class="manage-column"><strong><?= __('Key', $this->plugin) ?></strong></td></tr></thead><tbody id="varnishstats_<?=$server?>"></tbody></table>');
+                                delete data.timestamp;
+                                jQuery.each(data, function(key, val) {
+                                    jQuery('#varnishstats_<?=$server?>').append('<tr><td>'+val.description+'</td><td>'+val.value+'</td><td>'+key+'</td></tr>');
+                                });
+                            });
+                        </script>
                     <?php endforeach; ?>
-                </tbody>
-                <?php else: ?>
-                    <tr>
-                        <td colspan="3">
-                            <?=$error?>
-                        </td>
-                    </tr>
+                    <script type="text/javascript">
+                        jQuery('.nav-tab-wrapper > a.server').click(function(e){
+                            e.preventDefault();
+                            jQuery('.nav-tab-wrapper > a.server').removeClass('nav-tab-active');
+                            jQuery(this).addClass('nav-tab-active');
+                            jQuery(".servers").hide();
+                            jQuery("#server_" + jQuery(this).attr('server')).show();
+                        });
+                    </script>
                 <?php endif; ?>
-            </table>
-            <?php else: ?>
-                <?=$error?>
-            <?php endif; ?>
+            </div>
+        <?php elseif($_GET['tab'] == 'conf'): ?>
+            <form method="post" action="options.php">
+                <?php
+                    settings_fields($this->prefix . 'conf');
+                    do_settings_sections($this->prefix . 'conf');
+                    submit_button();
+                ?>
+            </form>
+            <form method="post" action="index.php?page=<?=$this->plugin?>-plugin&amp;tab=conf">
+                <?php
+                    settings_fields($this->prefix . 'download');
+                    do_settings_sections($this->prefix . 'download');
+                    submit_button(__('Download'));
+                ?>
+            </form>
         <?php endif; ?>
         </div>
     <?php
@@ -763,6 +641,8 @@
         }
         add_settings_field($this->prefix . "override", __("Override default TTL", $this->plugin), array($this, $this->prefix . "override"), $this->prefix . 'options', $this->prefix . 'options');
         add_settings_field($this->prefix . "purge_key", __("Purge key", $this->plugin), array($this, $this->prefix . "purge_key"), $this->prefix . 'options', $this->prefix . 'options');
+        add_settings_field($this->prefix . "cookie", __("Logged in cookie", $this->plugin), array($this, $this->prefix . "cookie"), $this->prefix . 'options', $this->prefix . 'options');
+        add_settings_field($this->prefix . "stats_json_file", __("Statistics JSONs", $this->plugin), array($this, $this->prefix . "stats_json_file"), $this->prefix . 'options', $this->prefix . 'options');
         add_settings_field($this->prefix . "debug", __("Enable debug", $this->plugin), array($this, $this->prefix . "debug"), $this->prefix . 'options', $this->prefix . 'options');
 
         if($_POST['option_page'] == $this->prefix . 'options') {
@@ -774,6 +654,8 @@
             register_setting($this->prefix . 'options', $this->prefix . "hosts");
             register_setting($this->prefix . 'options', $this->prefix . "override");
             register_setting($this->prefix . 'options', $this->prefix . "purge_key");
+            register_setting($this->prefix . 'options', $this->prefix . "cookie");
+            register_setting($this->prefix . 'options', $this->prefix . "stats_json_file");
             register_setting($this->prefix . 'options', $this->prefix . "debug");
         }
     }
@@ -842,8 +724,30 @@
     {
         ?>
             <input type="text" name="varnish_caching_purge_key" id="varnish_caching_purge_key" size="100" value="<?php echo get_option($this->prefix . 'purge_key'); ?>" />
+            <span onclick="generateHash(64, 0, 'varnish_caching_purge_key'); return false;" class="dashicons dashicons-image-rotate" title="<?=__('Generate')?>"></span>
             <p class="description">
-                <?=__('Key used to purge Varnish cache. It is sent to Varnish as X-VC-Purge-Key header. Use a SHA-256 hash.<br />If you can\'t use ACL\'s, use this option.', $this->plugin)?>
+                <?=__('Key used to purge Varnish cache. It is sent to Varnish as X-VC-Purge-Key header. Use a SHA-256 hash.<br />If you can\'t use ACL\'s, use this option. You can set the `purge key` in lib/purge.vcl.<br />Search the default value ff93c3cb929cee86901c7eefc8088e9511c005492c6502a930360c02221cf8f4 to find where to replace it.', $this->plugin)?>
+            </p>
+        <?php
+    }
+
+    public function varnish_caching_cookie()
+    {
+        ?>
+            <input type="text" name="varnish_caching_cookie" id="varnish_caching_cookie" size="10" maxlength="10" value="<?php echo get_option($this->prefix . 'cookie'); ?>" />
+            <span onclick="generateHash(10, 0, 'varnish_caching_cookie'); return false;" class="dashicons dashicons-image-rotate" title="<?=__('Generate')?>"></span>
+            <p class="description">
+                <?=__('This module sets a special cookie to tell Varnish that the user is logged in. This should be a random 10 chars string [0-9a-z]. You can set the `logged in cookie` in default.vcl.<br />Search the default value <i>c005492c65</i> to find where to replace it.', $this->plugin)?>
+            </p>
+        <?php
+    }
+
+    public function varnish_caching_stats_json_file()
+    {
+        ?>
+            <input type="text" name="varnish_caching_stats_json_file" id="varnish_caching_stats_json_file" size="100" value="<?php echo get_option($this->prefix . 'stats_json_file'); ?>" />
+            <p class="description">
+                <?=sprintf(__('Comma separated relative URLs. One for each IP. <a href="%1$s/wp-admin/index.php?page=vcaching-plugin&tab=stats&info=1">Click here</a> for more info on how to set this up.', $this->plugin), home_url())?>
             </p>
         <?php
     }
@@ -872,6 +776,158 @@
             <p class="description"><?=__('Relative URL to purge. Example : /wp-content/uploads/.*', $this->plugin)?></p>
         <?php
     }
+
+    public function conf_page_fields()
+    {
+        add_settings_section('conf', __("Varnish configuration", $this->plugin), null, $this->prefix . 'conf');
+
+        add_settings_field($this->prefix . "varnish_backends", __("Backends", $this->plugin), array($this, $this->prefix . "varnish_backends"), $this->prefix . 'conf', "conf");
+        add_settings_field($this->prefix . "varnish_acls", __("ACLs", $this->plugin), array($this, $this->prefix . "varnish_acls"), $this->prefix . 'conf', "conf");
+
+        if($_POST['option_page'] == $this->prefix . 'conf') {
+            register_setting($this->prefix . 'conf', $this->prefix . "varnish_backends");
+            register_setting($this->prefix . 'conf', $this->prefix . "varnish_acls");
+        }
+
+        add_settings_section('download', __("Get configuration files", $this->plugin), null, $this->prefix . 'download');
+
+        add_settings_field($this->prefix . "varnish_version", __("Version", $this->plugin), array($this, $this->prefix . "varnish_version"), $this->prefix . 'download', "download");
+
+        if($_POST['option_page'] == $this->prefix . 'download') {
+            $version = in_array($_POST['varnish_caching_varnish_version'], array(3,4)) ? $_POST['varnish_caching_varnish_version'] : 3;
+            $tmpfile = tempnam("tmp", "zip");
+            $zip = new ZipArchive();
+            $zip->open($tmpfile, ZipArchive::OVERWRITE);
+            $files = array(
+                'default.vcl' => true,
+                'LICENSE' => false,
+                'README.rst' => false,
+                'conf/acl.vcl' => true,
+                'conf/backend.vcl' => true,
+                'lib/bigfiles.vcl' => false,
+                'lib/bigfiles_pipe.vcl' => false,
+                'lib/cloudflare.vcl' => false,
+                'lib/mobile_cache.vcl' => false,
+                'lib/mobile_pass.vcl' => false,
+                'lib/purge.vcl' => true,
+                'lib/static.vcl' => false,
+                'lib/xforward.vcl' => false,
+            );
+            foreach ($files as $file => $parse) {
+                $filepath = __DIR__ . '/varnish-conf/v' . $version . '/' . $file;
+                if ($parse) {
+                    $content = $this->_parse_conf_file($version, $file, file_get_contents($filepath));
+                } else {
+                    $content = file_get_contents($filepath);
+                }
+                $zip->addFromString($file, $content);
+            }
+            $zip->close();
+            header('Content-Type: application/zip');
+            header('Content-Length: ' . filesize($tmpfile));
+            header('Content-Disposition: attachment; filename="varnish_v' . $version . '_conf.zip"');
+            readfile($tmpfile);
+            unlink($tmpfile);
+            exit();
+        }
+    }
+
+    public function varnish_caching_varnish_version()
+    {
+        ?>
+            <select name="varnish_caching_varnish_version" id="varnish_caching_varnish_version">
+                <option value="3">3</option>
+                <option value="4">4</option>
+            </select>
+            <p class="description"><?=__('Varnish Cache version', $this->plugin)?></p>
+        <?php
+    }
+
+    public function varnish_caching_varnish_backends()
+    {
+        ?>
+            <input type="text" name="varnish_caching_varnish_backends" id="varnish_caching_varnish_backends" size="100" value="<?php echo get_option($this->prefix . 'varnish_backends'); ?>" />
+            <p class="description"><?=__('Comma separated ip/ip:port. Example : 192.168.0.2,192.168.0.3:8080', $this->plugin)?></p>
+        <?php
+    }
+
+    public function varnish_caching_varnish_acls()
+    {
+        ?>
+            <input type="text" name="varnish_caching_varnish_acls" id="varnish_caching_varnish_acls" size="100" value="<?php echo get_option($this->prefix . 'varnish_acls'); ?>" />
+            <p class="description"><?=__('Comma separated ip/ip range. Example : 192.168.0.2,192.168.1.1/24', $this->plugin)?></p>
+        <?php
+    }
+
+    private function _parse_conf_file($version, $file, $content)
+    {
+        if ($file == 'default.vcl') {
+            $logged_in_cookie = get_option($this->prefix . 'cookie');
+            $content = str_replace('c005492c65', $logged_in_cookie, $content);
+        } else if ($file == 'conf/backend.vcl') {
+            if ($version == 3) {
+                $content = "";
+            } else if ($version == 4) {
+                $content = "import directors;\n\n";
+            }
+            $backend = array();
+            $ips = get_option($this->prefix . 'varnish_backends');
+            $ips = explode(',', $ips);
+            $id = 1;
+            foreach ($ips as $ip) {
+                if (strstr($ip, ":")) {
+                    $_ip = explode(':', $ip);
+                    $ip = $_ip[0];
+                    $port = $_ip[1];
+                } else {
+                    $port = 80;
+                }
+                $content .= "backend backend" . $id . " {\n";
+                $content .= "\t.host = \"" . $ip . "\";\n";
+                $content .= "\t.port = \"" . $port . "\";\n";
+                $content .= "}\n";
+                $backend[3] .= "\t{\n";
+                $backend[3] .= "\t\t.backend = backend" . $id . ";\n";
+                $backend[3] .= "\t}\n";
+                $backend[4] .= "\tbackends.add_backend(backend" . $id . ");\n";
+                $id++;
+            }
+            if ($version == 3) {
+                $content .= "\ndirector backends round-robin {\n";
+                $content .= $backend[3];
+                $content .= "}\n";
+                $content .= "\nsub vcl_recv {\n";
+                $content .= "\tset req.backend = backends;\n";
+                $content .= "}\n";
+            } elseif ($version == 4) {
+                $content .= "\nsub vcl_init {\n";
+                $content .= "\tnew backends = directors.round_robin();\n";
+                $content .= $backend[4];
+                $content .= "}\n";
+                $content .= "\nsub vcl_recv {\n";
+                $content .= "\tset req.backend_hint = backends.backend();\n";
+                $content .= "}\n";
+            }
+        } else if ($file == 'conf/acl.vcl') {
+            $acls = get_option($this->prefix . 'varnish_acls');
+            $acls = explode(',', $acls);
+            $content = "acl cloudflare {\n";
+            $content .= "\t# set this ip to your Railgun IP (if applicable)\n";
+            $content .= "\t# \"1.2.3.4\";\n";
+            $content .= "}\n";
+            $content .= "\nacl purge {\n";
+            $content .= "\t\"localhost\";\n";
+            $content .= "\t\"127.0.0.1\";\n";
+            foreach ($acls as $acl) {
+                $content .= "\t\"" . $acl . "\";\n";
+            }
+            $content .= "}\n";
+        } else if ($file == 'lib/purge.vcl') {
+            $purge_key = get_option($this->prefix . 'purge_key');
+            $content = str_replace('ff93c3cb929cee86901c7eefc8088e9511c005492c6502a930360c02221cf8f4', $purge_key, $content);
+        }
+        return $content;
+    }
 }
 
 $vcaching = new VCaching();

comments