imagecache: migrate configuration to new API
I started to do this as there was a possible issue with imagecache as a result of updates. However this has thus far not been proved, but still a useful update. Hopefully not too broken!
This commit is contained in:
parent
1b784355ad
commit
582e205bc0
13 changed files with 589 additions and 324 deletions
1
Makefile
1
Makefile
|
@ -123,6 +123,7 @@ SRCS += \
|
|||
src/api/api_mpegts.c \
|
||||
src/api/api_epg.c \
|
||||
src/api/api_epggrab.c \
|
||||
src/api/api_imagecache.c
|
||||
|
||||
SRCS += \
|
||||
src/parsers/parsers.c \
|
||||
|
|
|
@ -123,4 +123,5 @@ void api_init ( void )
|
|||
api_epg_init();
|
||||
api_epggrab_init();
|
||||
api_status_init();
|
||||
api_imagecache_init();
|
||||
}
|
||||
|
|
19
src/api.h
19
src/api.h
|
@ -55,15 +55,16 @@ int api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp );
|
|||
/*
|
||||
* Initialise
|
||||
*/
|
||||
void api_init ( void );
|
||||
void api_idnode_init ( void );
|
||||
void api_input_init ( void );
|
||||
void api_service_init ( void );
|
||||
void api_channel_init ( void );
|
||||
void api_mpegts_init ( void );
|
||||
void api_epg_init ( void );
|
||||
void api_epggrab_init ( void );
|
||||
void api_status_init ( void );
|
||||
void api_init ( void );
|
||||
void api_idnode_init ( void );
|
||||
void api_input_init ( void );
|
||||
void api_service_init ( void );
|
||||
void api_channel_init ( void );
|
||||
void api_mpegts_init ( void );
|
||||
void api_epg_init ( void );
|
||||
void api_epggrab_init ( void );
|
||||
void api_status_init ( void );
|
||||
void api_imagecache_init ( void );
|
||||
|
||||
/*
|
||||
* IDnode
|
||||
|
|
74
src/api/api_imagecache.c
Normal file
74
src/api/api_imagecache.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* API - Imagecache related calls
|
||||
*
|
||||
* Copyright (C) 2013 Adam Sutton
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; withm even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tvheadend.h"
|
||||
#include "channels.h"
|
||||
#include "access.h"
|
||||
#include "api.h"
|
||||
#include "imagecache.h"
|
||||
|
||||
#if ENABLE_IMAGECACHE
|
||||
|
||||
static int
|
||||
api_imagecache_load
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
htsmsg_t *l;
|
||||
pthread_mutex_lock(&global_lock);
|
||||
*resp = htsmsg_create_map();
|
||||
l = htsmsg_create_list();
|
||||
htsmsg_add_msg(l, NULL, imagecache_get_config());
|
||||
htsmsg_add_msg(*resp, "entries", l);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
api_imagecache_save
|
||||
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
|
||||
{
|
||||
pthread_mutex_lock(&global_lock);
|
||||
if (imagecache_set_config(args))
|
||||
imagecache_save();
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
*resp = htsmsg_create_map();
|
||||
htsmsg_add_u32(*resp, "success", 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
api_imagecache_init ( void )
|
||||
{
|
||||
static api_hook_t ah[] = {
|
||||
{ "imagecache/config/load", ACCESS_ADMIN, api_imagecache_load, NULL },
|
||||
{ "imagecache/config/save", ACCESS_ADMIN, api_imagecache_save, NULL },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
api_register_all(ah);
|
||||
}
|
||||
|
||||
#else /* ENABLE_IMAGECACHE */
|
||||
|
||||
void
|
||||
api_imagecache_init ( void )
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
509
src/imagecache.c
509
src/imagecache.c
|
@ -33,6 +33,8 @@
|
|||
#include "imagecache.h"
|
||||
#include "queue.h"
|
||||
#include "redblack.h"
|
||||
#include "notify.h"
|
||||
#include "prop.h"
|
||||
|
||||
#if ENABLE_IMAGECACHE
|
||||
#define CURL_STATICLIB
|
||||
|
@ -40,10 +42,6 @@
|
|||
#include <curl/easy.h>
|
||||
#endif
|
||||
|
||||
// TODO: icon cache flushing?
|
||||
// TODO: md5 validation?
|
||||
// TODO: allow cache to be disabled by users
|
||||
|
||||
/*
|
||||
* Image metadata
|
||||
*/
|
||||
|
@ -64,41 +62,191 @@ typedef struct imagecache_image
|
|||
RB_ENTRY(imagecache_image) url_link; ///< Index by URL
|
||||
} imagecache_image_t;
|
||||
|
||||
static int _imagecache_id;
|
||||
static RB_HEAD(,imagecache_image) _imagecache_by_id;
|
||||
static RB_HEAD(,imagecache_image) _imagecache_by_url;
|
||||
|
||||
pthread_mutex_t imagecache_mutex;
|
||||
|
||||
static void _imagecache_save ( imagecache_image_t *img );
|
||||
static int imagecache_id;
|
||||
static RB_HEAD(,imagecache_image) imagecache_by_id;
|
||||
static RB_HEAD(,imagecache_image) imagecache_by_url;
|
||||
|
||||
#if ENABLE_IMAGECACHE
|
||||
uint32_t imagecache_enabled;
|
||||
uint32_t imagecache_ok_period;
|
||||
uint32_t imagecache_fail_period;
|
||||
uint32_t imagecache_ignore_sslcert;
|
||||
struct imagecache_config imagecache_conf;
|
||||
static const property_t imagecache_props[] = {
|
||||
{
|
||||
.type = PT_BOOL,
|
||||
.id = "enabled",
|
||||
.name = "Enabled",
|
||||
.off = offsetof(struct imagecache_config, enabled),
|
||||
},
|
||||
{
|
||||
.type = PT_BOOL,
|
||||
.id = "ignore_sslcert",
|
||||
.name = "Ignore invalid SSL certificate",
|
||||
.off = offsetof(struct imagecache_config, ignore_sslcert),
|
||||
},
|
||||
{
|
||||
.type = PT_U32,
|
||||
.id = "ok_period",
|
||||
.name = "Re-try period",
|
||||
.off = offsetof(struct imagecache_config, ok_period),
|
||||
},
|
||||
{
|
||||
.type = PT_U32,
|
||||
.id = "fail_period",
|
||||
.name = "Re-try period of failed images",
|
||||
.off = offsetof(struct imagecache_config, fail_period),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static pthread_cond_t _imagecache_cond;
|
||||
static TAILQ_HEAD(, imagecache_image) _imagecache_queue;
|
||||
static void _imagecache_add ( imagecache_image_t *img );
|
||||
static void* _imagecache_thread ( void *p );
|
||||
static int _imagecache_fetch ( imagecache_image_t *img );
|
||||
static pthread_cond_t imagecache_cond;
|
||||
static TAILQ_HEAD(, imagecache_image) imagecache_queue;
|
||||
static gtimer_t imagecache_timer;
|
||||
#endif
|
||||
|
||||
static int _url_cmp ( void *a, void *b )
|
||||
static int
|
||||
url_cmp ( imagecache_image_t *a, imagecache_image_t *b )
|
||||
{
|
||||
return strcmp(((imagecache_image_t*)a)->url, ((imagecache_image_t*)b)->url);
|
||||
return strcmp(a->url, b->url);
|
||||
}
|
||||
|
||||
static int _id_cmp ( void *a, void *b )
|
||||
static int
|
||||
id_cmp ( imagecache_image_t *a, imagecache_image_t *b )
|
||||
{
|
||||
return ((imagecache_image_t*)a)->id - ((imagecache_image_t*)b)->id;
|
||||
return (a->id - b->id);
|
||||
}
|
||||
|
||||
static void
|
||||
imagecache_image_save ( imagecache_image_t *img )
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_str(m, "url", img->url);
|
||||
if (img->updated)
|
||||
htsmsg_add_s64(m, "updated", img->updated);
|
||||
hts_settings_save(m, "imagecache/meta/%d", img->id);
|
||||
}
|
||||
|
||||
#if ENABLE_IMAGECACHE
|
||||
static void
|
||||
imagecache_image_add ( imagecache_image_t *img )
|
||||
{
|
||||
if (strncasecmp("file://", img->url, 7)) {
|
||||
img->state = QUEUED;
|
||||
TAILQ_INSERT_TAIL(&imagecache_queue, img, q_link);
|
||||
pthread_cond_broadcast(&imagecache_cond);
|
||||
} else {
|
||||
time(&img->updated);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
imagecache_image_fetch ( imagecache_image_t *img )
|
||||
{
|
||||
int res = 1;
|
||||
CURL *curl;
|
||||
FILE *fp;
|
||||
char tmp[256], path[256];
|
||||
|
||||
/* Open file */
|
||||
if (hts_settings_buildpath(path, sizeof(path), "imagecache/data/%d",
|
||||
img->id))
|
||||
goto error;
|
||||
if (hts_settings_makedirs(path))
|
||||
goto error;
|
||||
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
|
||||
if (!(fp = fopen(tmp, "wb")))
|
||||
goto error;
|
||||
|
||||
/* Build command */
|
||||
tvhlog(LOG_DEBUG, "imagecache", "fetch %s", img->url);
|
||||
curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_URL, img->url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "TVHeadend");
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
||||
if (imagecache_conf.ignore_sslcert)
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
|
||||
/* Fetch (release lock, incase of delays) */
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(fp);
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
/* Process */
|
||||
error:
|
||||
img->state = IDLE;
|
||||
time(&img->updated); // even if failed (possibly request sooner?)
|
||||
if (res) {
|
||||
img->failed = 1;
|
||||
unlink(tmp);
|
||||
tvhlog(LOG_WARNING, "imagecache", "failed to download %s", img->url);
|
||||
} else {
|
||||
img->failed = 0;
|
||||
unlink(path);
|
||||
rename(tmp, path);
|
||||
tvhlog(LOG_DEBUG, "imagecache", "downloaded %s", img->url);
|
||||
}
|
||||
imagecache_image_save(img);
|
||||
pthread_cond_broadcast(&imagecache_cond);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
static void *
|
||||
imagecache_thread ( void *p )
|
||||
{
|
||||
imagecache_image_t *img;
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
while (1) {
|
||||
|
||||
/* Check we're enabled */
|
||||
if (!imagecache_conf.enabled) {
|
||||
pthread_cond_wait(&imagecache_cond, &global_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get entry */
|
||||
if (!(img = TAILQ_FIRST(&imagecache_queue))) {
|
||||
pthread_cond_wait(&imagecache_cond, &global_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process */
|
||||
img->state = FETCHING;
|
||||
TAILQ_REMOVE(&imagecache_queue, img, q_link);
|
||||
|
||||
/* Fetch */
|
||||
(void)imagecache_image_fetch(img);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
imagecache_timer_cb ( void *p )
|
||||
{
|
||||
time_t now, when;
|
||||
imagecache_image_t *img;
|
||||
time(&now);
|
||||
RB_FOREACH(img, &imagecache_by_url, url_link) {
|
||||
if (img->state != IDLE) continue;
|
||||
when = img->failed ? imagecache_conf.fail_period
|
||||
: imagecache_conf.ok_period;
|
||||
when = img->updated + (when * 3600);
|
||||
if (when < now)
|
||||
imagecache_image_add(img);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ENABLE_IMAGECACHE */
|
||||
|
||||
/*
|
||||
* Initialise
|
||||
*/
|
||||
void imagecache_init ( void )
|
||||
void
|
||||
imagecache_init ( void )
|
||||
{
|
||||
htsmsg_t *m, *e;
|
||||
htsmsg_field_t *f;
|
||||
|
@ -107,28 +255,24 @@ void imagecache_init ( void )
|
|||
uint32_t id;
|
||||
|
||||
/* Init vars */
|
||||
_imagecache_id = 0;
|
||||
imagecache_id = 0;
|
||||
#if ENABLE_IMAGECACHE
|
||||
imagecache_enabled = 0;
|
||||
imagecache_ok_period = 24 * 7; // weekly
|
||||
imagecache_fail_period = 24; // daily
|
||||
imagecache_ignore_sslcert = 0;
|
||||
imagecache_conf.enabled = 0;
|
||||
imagecache_conf.ok_period = 24 * 7; // weekly
|
||||
imagecache_conf.fail_period = 24; // daily
|
||||
imagecache_conf.ignore_sslcert = 0;
|
||||
#endif
|
||||
|
||||
/* Create threads */
|
||||
pthread_mutex_init(&imagecache_mutex, NULL);
|
||||
#if ENABLE_IMAGECACHE
|
||||
pthread_cond_init(&_imagecache_cond, NULL);
|
||||
TAILQ_INIT(&_imagecache_queue);
|
||||
pthread_cond_init(&imagecache_cond, NULL);
|
||||
TAILQ_INIT(&imagecache_queue);
|
||||
#endif
|
||||
|
||||
/* Load settings */
|
||||
#if ENABLE_IMAGECACHE
|
||||
if ((m = hts_settings_load("imagecache/config"))) {
|
||||
htsmsg_get_u32(m, "enabled", &imagecache_enabled);
|
||||
htsmsg_get_u32(m, "ok_period", &imagecache_ok_period);
|
||||
htsmsg_get_u32(m, "fail_period", &imagecache_fail_period);
|
||||
htsmsg_get_u32(m, "ignore_sslcert", &imagecache_ignore_sslcert);
|
||||
imagecache_set_config(m);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
#endif
|
||||
|
@ -141,7 +285,7 @@ void imagecache_init ( void )
|
|||
img->id = id;
|
||||
img->url = strdup(url);
|
||||
img->updated = htsmsg_get_s64_or_default(e, "updated", 0);
|
||||
i = RB_INSERT_SORTED(&_imagecache_by_url, img, url_link, _url_cmp);
|
||||
i = RB_INSERT_SORTED(&imagecache_by_url, img, url_link, url_cmp);
|
||||
if (i) {
|
||||
hts_settings_remove("imagecache/meta/%d", id);
|
||||
hts_settings_remove("imagecache/data/%d", id);
|
||||
|
@ -149,13 +293,13 @@ void imagecache_init ( void )
|
|||
free(img);
|
||||
continue;
|
||||
}
|
||||
i = RB_INSERT_SORTED(&_imagecache_by_id, img, id_link, _id_cmp);
|
||||
i = RB_INSERT_SORTED(&imagecache_by_id, img, id_link, id_cmp);
|
||||
assert(!i);
|
||||
if (id > _imagecache_id)
|
||||
_imagecache_id = id;
|
||||
if (id > imagecache_id)
|
||||
imagecache_id = id;
|
||||
#if ENABLE_IMAGECACHE
|
||||
if (!img->updated)
|
||||
_imagecache_add(img);
|
||||
imagecache_image_add(img);
|
||||
#endif
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
|
@ -165,81 +309,65 @@ void imagecache_init ( void )
|
|||
#if ENABLE_IMAGECACHE
|
||||
{
|
||||
pthread_t tid;
|
||||
tvhthread_create(&tid, NULL, _imagecache_thread, NULL, 1);
|
||||
tvhthread_create(&tid, NULL, imagecache_thread, NULL, 1);
|
||||
}
|
||||
|
||||
/* Re-try timer */
|
||||
// TODO: this could be more efficient by being targetted, however
|
||||
// the reality its not necessary and I'd prefer to avoid dumping
|
||||
// 100's of timers into the global pool
|
||||
gtimer_arm(&imagecache_timer, imagecache_timer_cb, NULL, 600);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Save settings
|
||||
*/
|
||||
#if ENABLE_IMAGECACHE
|
||||
void imagecache_save ( void )
|
||||
|
||||
/*
|
||||
* Get config
|
||||
*/
|
||||
htsmsg_t *
|
||||
imagecache_get_config ( void )
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_add_u32(m, "enabled", imagecache_enabled);
|
||||
htsmsg_add_u32(m, "ok_period", imagecache_ok_period);
|
||||
htsmsg_add_u32(m, "fail_period", imagecache_fail_period);
|
||||
htsmsg_add_u32(m, "ignore_sslcert", imagecache_ignore_sslcert);
|
||||
prop_read_values(&imagecache_conf, imagecache_props, m, 0, NULL);
|
||||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set config
|
||||
*/
|
||||
int
|
||||
imagecache_set_config ( htsmsg_t *m )
|
||||
{
|
||||
int save = prop_write_values(&imagecache_conf, imagecache_props, m, 0, NULL);
|
||||
if (save)
|
||||
pthread_cond_broadcast(&imagecache_cond);
|
||||
return save;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save
|
||||
*/
|
||||
void
|
||||
imagecache_save ( void )
|
||||
{
|
||||
htsmsg_t *m = imagecache_get_config();
|
||||
hts_settings_save(m, "imagecache/config");
|
||||
notify_reload("imagecache");
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable/disable
|
||||
*/
|
||||
int imagecache_set_enabled ( uint32_t e )
|
||||
{
|
||||
if (e == imagecache_enabled)
|
||||
return 0;
|
||||
imagecache_enabled = e;
|
||||
if (e)
|
||||
pthread_cond_broadcast(&_imagecache_cond);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set ok period
|
||||
*/
|
||||
int imagecache_set_ok_period ( uint32_t p )
|
||||
{
|
||||
if (p == imagecache_ok_period)
|
||||
return 0;
|
||||
imagecache_ok_period = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set fail period
|
||||
*/
|
||||
int imagecache_set_fail_period ( uint32_t p )
|
||||
{
|
||||
if (p == imagecache_fail_period)
|
||||
return 0;
|
||||
imagecache_fail_period = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set ignore SSL cert
|
||||
*/
|
||||
int imagecache_set_ignore_sslcert ( uint32_t p )
|
||||
{
|
||||
if (p == imagecache_ignore_sslcert)
|
||||
return 0;
|
||||
imagecache_ignore_sslcert = p;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fetch a URLs ID
|
||||
*/
|
||||
uint32_t imagecache_get_id ( const char *url )
|
||||
uint32_t
|
||||
imagecache_get_id ( const char *url )
|
||||
{
|
||||
uint32_t id = 0;
|
||||
imagecache_image_t *i;
|
||||
static imagecache_image_t *skel = NULL;
|
||||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
/* Invalid */
|
||||
if (!url)
|
||||
return 0;
|
||||
|
@ -256,27 +384,25 @@ uint32_t imagecache_get_id ( const char *url )
|
|||
skel->url = url;
|
||||
|
||||
/* Create/Find */
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
i = RB_INSERT_SORTED(&_imagecache_by_url, skel, url_link, _url_cmp);
|
||||
i = RB_INSERT_SORTED(&imagecache_by_url, skel, url_link, url_cmp);
|
||||
if (!i) {
|
||||
i = skel;
|
||||
i->url = strdup(url);
|
||||
i->id = ++_imagecache_id;
|
||||
skel = RB_INSERT_SORTED(&_imagecache_by_id, i, id_link, _id_cmp);
|
||||
i->id = ++imagecache_id;
|
||||
skel = RB_INSERT_SORTED(&imagecache_by_id, i, id_link, id_cmp);
|
||||
assert(!skel);
|
||||
#if ENABLE_IMAGECACHE
|
||||
_imagecache_add(i);
|
||||
imagecache_image_add(i);
|
||||
#endif
|
||||
_imagecache_save(i);
|
||||
imagecache_image_save(i);
|
||||
}
|
||||
#if ENABLE_IMAGECACHE
|
||||
if (!strncasecmp(url, "file://", 7) || imagecache_enabled)
|
||||
if (!strncasecmp(url, "file://", 7) || imagecache_conf.enabled)
|
||||
id = i->id;
|
||||
#else
|
||||
if (!strncasecmp(url, "file://", 7))
|
||||
id = i->id;
|
||||
#endif
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
@ -284,22 +410,18 @@ uint32_t imagecache_get_id ( const char *url )
|
|||
/*
|
||||
* Get data
|
||||
*/
|
||||
int imagecache_open ( uint32_t id )
|
||||
int
|
||||
imagecache_open ( uint32_t id )
|
||||
{
|
||||
imagecache_image_t skel, *i;
|
||||
int fd = -1;
|
||||
int e, fd = -1;
|
||||
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
lock_assert(&global_lock);
|
||||
|
||||
/* Find */
|
||||
skel.id = id;
|
||||
i = RB_FIND(&_imagecache_by_id, &skel, id_link, _id_cmp);
|
||||
|
||||
/* Invalid */
|
||||
if (!i) {
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
if (!(i = RB_FIND(&imagecache_by_id, &skel, id_link, id_cmp)))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Local file */
|
||||
if (!strncasecmp(i->url, "file://", 7))
|
||||
|
@ -307,158 +429,37 @@ int imagecache_open ( uint32_t id )
|
|||
|
||||
/* Remote file */
|
||||
#if ENABLE_IMAGECACHE
|
||||
else if (imagecache_enabled) {
|
||||
else if (imagecache_conf.enabled) {
|
||||
struct timespec ts;
|
||||
int err;
|
||||
|
||||
/* Use existing */
|
||||
if (i->updated) {
|
||||
// use existing
|
||||
|
||||
/* Wait */
|
||||
} else if (i->state == FETCHING) {
|
||||
ts.tv_nsec = 0;
|
||||
time(&ts.tv_sec);
|
||||
ts.tv_sec += 10; // TODO: sensible timeout?
|
||||
err = pthread_cond_timedwait(&_imagecache_cond, &imagecache_mutex, &ts);
|
||||
if (err == ETIMEDOUT) {
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
ts.tv_nsec = 0;
|
||||
ts.tv_sec += 5;
|
||||
err = pthread_cond_timedwait(&imagecache_cond, &global_lock, &ts);
|
||||
if (err == ETIMEDOUT)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Attempt to fetch */
|
||||
} else if (i->state == QUEUED) {
|
||||
i->state = FETCHING;
|
||||
TAILQ_REMOVE(&_imagecache_queue, i, q_link);
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
if (_imagecache_fetch(i))
|
||||
TAILQ_REMOVE(&imagecache_queue, i, q_link);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
e = imagecache_image_fetch(i);
|
||||
pthread_mutex_lock(&global_lock);
|
||||
if (e)
|
||||
return -1;
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
}
|
||||
fd = hts_settings_open_file(0, "imagecache/data/%d", i->id);
|
||||
}
|
||||
#endif
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void _imagecache_save ( imagecache_image_t *img )
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
|
||||
htsmsg_add_str(m, "url", img->url);
|
||||
if (img->updated)
|
||||
htsmsg_add_s64(m, "updated", img->updated);
|
||||
|
||||
hts_settings_save(m, "imagecache/meta/%d", img->id);
|
||||
}
|
||||
|
||||
#if ENABLE_IMAGECACHE
|
||||
static void _imagecache_add ( imagecache_image_t *img )
|
||||
{
|
||||
if (strncasecmp("file://", img->url, 7)) {
|
||||
img->state = QUEUED;
|
||||
TAILQ_INSERT_TAIL(&_imagecache_queue, img, q_link);
|
||||
pthread_cond_broadcast(&_imagecache_cond);
|
||||
} else {
|
||||
time(&img->updated);
|
||||
}
|
||||
}
|
||||
|
||||
static void *_imagecache_thread ( void *p )
|
||||
{
|
||||
int err;
|
||||
imagecache_image_t *img;
|
||||
struct timespec ts;
|
||||
ts.tv_nsec = 0;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Get entry */
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
if (!imagecache_enabled) {
|
||||
pthread_cond_wait(&_imagecache_cond, &imagecache_mutex);
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
continue;
|
||||
}
|
||||
img = TAILQ_FIRST(&_imagecache_queue);
|
||||
if (!img) {
|
||||
time(&ts.tv_sec);
|
||||
ts.tv_sec += 60;
|
||||
err = pthread_cond_timedwait(&_imagecache_cond, &imagecache_mutex, &ts);
|
||||
if (err == ETIMEDOUT) {
|
||||
uint32_t period;
|
||||
RB_FOREACH(img, &_imagecache_by_url, url_link) {
|
||||
if (img->state != IDLE) continue;
|
||||
period = img->failed ? imagecache_fail_period : imagecache_ok_period;
|
||||
period *= 3600;
|
||||
if (period && ((ts.tv_sec - img->updated) > period))
|
||||
_imagecache_add(img);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
continue;
|
||||
}
|
||||
img->state = FETCHING;
|
||||
TAILQ_REMOVE(&_imagecache_queue, img, q_link);
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
|
||||
/* Fetch */
|
||||
_imagecache_fetch(img);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _imagecache_fetch ( imagecache_image_t *img )
|
||||
{
|
||||
int res;
|
||||
CURL *curl;
|
||||
FILE *fp;
|
||||
char tmp[256], path[256];
|
||||
|
||||
/* Open file */
|
||||
if (hts_settings_buildpath(path, sizeof(path), "imagecache/data/%d",
|
||||
img->id))
|
||||
return 1;
|
||||
if (hts_settings_makedirs(path))
|
||||
return 1;
|
||||
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
|
||||
if (!(fp = fopen(tmp, "wb")))
|
||||
return 1;
|
||||
|
||||
/* Build command */
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
tvhlog(LOG_DEBUG, "imagecache", "fetch %s", img->url);
|
||||
curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_URL, img->url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "TVHeadend");
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
||||
if (imagecache_ignore_sslcert)
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
|
||||
/* Fetch */
|
||||
res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(fp);
|
||||
|
||||
/* Process */
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
img->state = IDLE;
|
||||
time(&img->updated); // even if failed (possibly request sooner?)
|
||||
if (res) {
|
||||
img->failed = 1;
|
||||
unlink(tmp);
|
||||
tvhlog(LOG_WARNING, "imagecache", "failed to download %s", img->url);
|
||||
} else {
|
||||
img->failed = 0;
|
||||
unlink(path);
|
||||
rename(tmp, path);
|
||||
tvhlog(LOG_DEBUG, "imagecache", "downloaded %s", img->url);
|
||||
}
|
||||
_imagecache_save(img);
|
||||
pthread_cond_broadcast(&_imagecache_cond);
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
|
||||
return res;
|
||||
};
|
||||
#endif
|
||||
#endif /* ENABLE_IMAGECACHE */
|
||||
|
|
|
@ -21,25 +21,22 @@
|
|||
|
||||
#include <pthread.h>
|
||||
|
||||
extern uint32_t imagecache_enabled;
|
||||
extern uint32_t imagecache_ok_period;
|
||||
extern uint32_t imagecache_fail_period;
|
||||
extern uint32_t imagecache_ignore_sslcert;
|
||||
struct imagecache_config {
|
||||
int enabled;
|
||||
int ignore_sslcert;
|
||||
uint32_t ok_period;
|
||||
uint32_t fail_period;
|
||||
};
|
||||
|
||||
extern struct imagecache_config imagecache_conf;
|
||||
|
||||
extern pthread_mutex_t imagecache_mutex;
|
||||
|
||||
void imagecache_init ( void );
|
||||
|
||||
void imagecache_save ( void );
|
||||
|
||||
int imagecache_set_enabled ( uint32_t e )
|
||||
__attribute__((warn_unused_result));
|
||||
int imagecache_set_ok_period ( uint32_t e )
|
||||
__attribute__((warn_unused_result));
|
||||
int imagecache_set_fail_period ( uint32_t e )
|
||||
__attribute__((warn_unused_result));
|
||||
int imagecache_set_ignore_sslcert ( uint32_t e )
|
||||
__attribute__((warn_unused_result));
|
||||
htsmsg_t *imagecache_get_config ( void );
|
||||
int imagecache_set_config ( htsmsg_t *c );
|
||||
void imagecache_save ( void );
|
||||
|
||||
// Note: will return 0 if invalid (must serve original URL)
|
||||
uint32_t imagecache_get_id ( const char *url );
|
||||
|
|
|
@ -135,7 +135,7 @@ const tvh_caps_t tvheadend_capabilities[] = {
|
|||
{ "transcoding", &transcoding_enabled },
|
||||
#endif
|
||||
#if ENABLE_IMAGECACHE
|
||||
{ "imagecache", &imagecache_enabled },
|
||||
{ "imagecache", (uint32_t*)&imagecache_conf.enabled },
|
||||
#endif
|
||||
#if ENABLE_TIMESHIFT
|
||||
{ "timeshift", ×hift_enabled },
|
||||
|
|
|
@ -104,6 +104,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
|
|||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"static/livegrid/resources/css/ext-ux-livegrid.css\">\n"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"static/extjs/examples/ux/gridfilters/css/GridFilters.css\">\n"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"static/extjs/examples/ux/gridfilters/css/RangeMenu.css\">\n"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"static/xcheckbox/xcheckbox.css\">\n"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"static/app/ext.css\">\n",
|
||||
tvheadend_webui_debug ? "-debug" : "",
|
||||
tvheadend_webui_debug ? "-debug" : "",
|
||||
|
@ -119,6 +120,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
|
|||
extjs_load(hq, "static/lovcombo/lovcombo-all.js");
|
||||
extjs_load(hq, "static/multiselect/multiselect.js");
|
||||
extjs_load(hq, "static/multiselect/ddview.js");
|
||||
extjs_load(hq, "static/xcheckbox/xcheckbox.js");
|
||||
extjs_load(hq, "static/checkcolumn/CheckColumn.js");
|
||||
extjs_load(hq, "static/extjs/examples/ux/gridfilters/GridFilters.js");
|
||||
extjs_load(hq, "static/extjs/examples/ux/gridfilters/filter/Filter.js");
|
||||
|
@ -1506,16 +1508,6 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque)
|
|||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
/* Image cache */
|
||||
#if ENABLE_IMAGECACHE
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
htsmsg_add_u32(m, "imagecache_enabled", imagecache_enabled);
|
||||
htsmsg_add_u32(m, "imagecache_ok_period", imagecache_ok_period);
|
||||
htsmsg_add_u32(m, "imagecache_fail_period", imagecache_fail_period);
|
||||
htsmsg_add_u32(m, "imagecache_ignore_sslcert", imagecache_ignore_sslcert);
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
#endif
|
||||
|
||||
if (!m) return HTTP_STATUS_BAD_REQUEST;
|
||||
out = json_single_record(m, "config");
|
||||
|
||||
|
@ -1550,22 +1542,6 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque)
|
|||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
/* Image Cache */
|
||||
#if ENABLE_IMAGECACHE
|
||||
pthread_mutex_lock(&imagecache_mutex);
|
||||
str = http_arg_get(&hc->hc_req_args, "imagecache_enabled");
|
||||
save = imagecache_set_enabled(!!str);
|
||||
if ((str = http_arg_get(&hc->hc_req_args, "imagecache_ok_period")))
|
||||
save |= imagecache_set_ok_period(atoi(str));
|
||||
if ((str = http_arg_get(&hc->hc_req_args, "imagecache_fail_period")))
|
||||
save |= imagecache_set_fail_period(atoi(str));
|
||||
str = http_arg_get(&hc->hc_req_args, "imagecache_ignore_sslcert");
|
||||
save |= imagecache_set_ignore_sslcert(!!str);
|
||||
if (save)
|
||||
imagecache_save();
|
||||
pthread_mutex_unlock(&imagecache_mutex);
|
||||
#endif
|
||||
|
||||
out = htsmsg_create_map();
|
||||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
|
|
|
@ -32,17 +32,26 @@ tvheadend.comet.on('config', function(m) {
|
|||
});
|
||||
|
||||
tvheadend.miscconf = function() {
|
||||
|
||||
/*
|
||||
* Basic Config
|
||||
*/
|
||||
var confreader = new Ext.data.JsonReader({
|
||||
root : 'config'
|
||||
}, [ 'muxconfpath', 'language',
|
||||
'imagecache_enabled', 'imagecache_ok_period',
|
||||
'imagecache_fail_period', 'imagecache_ignore_sslcert',
|
||||
'tvhtime_update_enabled', 'tvhtime_ntp_enabled',
|
||||
'tvhtime_tolerance', 'transcoding_enabled']);
|
||||
|
||||
/*
|
||||
* Imagecache
|
||||
*/
|
||||
var imagecache_reader = new Ext.data.JsonReader({
|
||||
root : 'entries'
|
||||
},
|
||||
[
|
||||
'enabled', 'ok_period', 'fail_period', 'ignore_sslcert',
|
||||
]);
|
||||
|
||||
/* ****************************************************************
|
||||
* Form Fields
|
||||
* ***************************************************************/
|
||||
|
@ -106,23 +115,23 @@ tvheadend.miscconf = function() {
|
|||
/*
|
||||
* Image cache
|
||||
*/
|
||||
var imagecacheEnabled = new Ext.form.Checkbox({
|
||||
name: 'imagecache_enabled',
|
||||
var imagecacheEnabled = new Ext.ux.form.XCheckbox({
|
||||
name: 'enabled',
|
||||
fieldLabel: 'Enabled',
|
||||
});
|
||||
|
||||
var imagecacheOkPeriod = new Ext.form.NumberField({
|
||||
name: 'imagecache_ok_period',
|
||||
name: 'ok_period',
|
||||
fieldLabel: 'Re-fetch period (hours)'
|
||||
});
|
||||
|
||||
var imagecacheFailPeriod = new Ext.form.NumberField({
|
||||
name: 'imagecache_fail_period',
|
||||
name: 'fail_period',
|
||||
fieldLabel: 'Re-try period (hours)',
|
||||
});
|
||||
|
||||
var imagecacheIgnoreSSLCert = new Ext.form.Checkbox({
|
||||
name: 'imagecache_ignore_sslcert',
|
||||
var imagecacheIgnoreSSLCert = new Ext.ux.form.XCheckbox({
|
||||
name: 'ignore_sslcert',
|
||||
fieldLabel: 'Ignore invalid SSL certificate'
|
||||
});
|
||||
|
||||
|
@ -137,6 +146,17 @@ tvheadend.miscconf = function() {
|
|||
if (tvheadend.capabilities.indexOf('imagecache') == -1)
|
||||
imagecachePanel.hide();
|
||||
|
||||
var imagecache_form = new Ext.form.FormPanel({
|
||||
border : false,
|
||||
labelAlign : 'left',
|
||||
labelWidth : 200,
|
||||
waitMsgTarget : true,
|
||||
reader: imagecache_reader,
|
||||
layout : 'form',
|
||||
defaultType : 'textfield',
|
||||
autoHeight : true,
|
||||
items : [ imagecachePanel ]
|
||||
});
|
||||
|
||||
/*
|
||||
* Transcoding
|
||||
|
@ -176,23 +196,29 @@ tvheadend.miscconf = function() {
|
|||
});
|
||||
|
||||
var confpanel = new Ext.form.FormPanel({
|
||||
title : 'General',
|
||||
iconCls : 'wrench',
|
||||
border : false,
|
||||
bodyStyle : 'padding:15px',
|
||||
labelAlign : 'left',
|
||||
labelWidth : 200,
|
||||
border : false,
|
||||
waitMsgTarget : true,
|
||||
reader : confreader,
|
||||
layout : 'form',
|
||||
defaultType : 'textfield',
|
||||
autoHeight : true,
|
||||
items : [ language, dvbscanPath,
|
||||
imagecachePanel, tvhtimePanel,
|
||||
transcodingPanel],
|
||||
tbar : [ saveButton, '->', helpButton ]
|
||||
tvhtimePanel,
|
||||
transcodingPanel]
|
||||
});
|
||||
|
||||
var panel = new Ext.Panel({
|
||||
title : 'General',
|
||||
iconCls : 'wrench',
|
||||
border : false,
|
||||
bodyStyle : 'padding:15px',
|
||||
layout : 'form',
|
||||
items: [confpanel, imagecache_form ],
|
||||
tbar : [ saveButton, '->', helpButton ]
|
||||
});
|
||||
|
||||
/* ****************************************************************
|
||||
* Load/Save
|
||||
* ***************************************************************/
|
||||
|
@ -207,6 +233,15 @@ tvheadend.miscconf = function() {
|
|||
confpanel.enable();
|
||||
}
|
||||
});
|
||||
imagecache_form.getForm().load({
|
||||
url : 'api/imagecache/config/load',
|
||||
success : function (form, action) {
|
||||
imagecache_form.enable();
|
||||
},
|
||||
failure : function (form, action) {
|
||||
alert("FAILED");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function saveChanges() {
|
||||
|
@ -220,7 +255,14 @@ tvheadend.miscconf = function() {
|
|||
Ext.Msg.alert('Save failed', action.result.errormsg);
|
||||
}
|
||||
});
|
||||
imagecache_form.getForm().submit({
|
||||
url : 'api/imagecache/config/save',
|
||||
waitMsg : 'Saving data...',
|
||||
failure : function(form, action) {
|
||||
Ext.Msg.alert('Imagecache save failed', action.result.errormsg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return confpanel;
|
||||
return panel;
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ tvheadend.IdNodeField = function (conf)
|
|||
} else {
|
||||
switch (this.type) {
|
||||
case 'bool':
|
||||
cons = Ext.form.Checkbox;
|
||||
cons = Ext.ux.form.XCheckbox;
|
||||
break;
|
||||
|
||||
case 'int':
|
||||
|
@ -330,7 +330,7 @@ tvheadend.idnode_editor_field = function(f, create)
|
|||
break;
|
||||
|
||||
case 'bool':
|
||||
return new Ext.form.Checkbox({
|
||||
return new Ext.ux.form.XCheckbox({
|
||||
fieldLabel : f.caption,
|
||||
name : f.id,
|
||||
checked : value,
|
||||
|
@ -865,7 +865,7 @@ tvheadend.idnode_grid = function(panel, conf)
|
|||
}
|
||||
|
||||
/* Grid Panel */
|
||||
var auto = new Ext.form.Checkbox({
|
||||
var auto = new Ext.ux.form.XCheckbox({
|
||||
checked : true,
|
||||
listeners : {
|
||||
check : function ( s, c ) {
|
||||
|
|
25
src/webui/static/xcheckbox/xcheckbox.css
Normal file
25
src/webui/static/xcheckbox/xcheckbox.css
Normal file
|
@ -0,0 +1,25 @@
|
|||
.xcheckbox-wrap {
|
||||
line-height: 18px;
|
||||
padding-top:2px;
|
||||
}
|
||||
.xcheckbox-wrap a {
|
||||
display:block;
|
||||
width:16px;
|
||||
height:16px;
|
||||
float:left;
|
||||
}
|
||||
.x-toolbar .xcheckbox-wrap {
|
||||
padding: 0 0 2px 0;
|
||||
}
|
||||
.xcheckbox-on {
|
||||
background:transparent url(../extjs/resources/images/default/menu/checked.gif) no-repeat 0 0;
|
||||
}
|
||||
.xcheckbox-off {
|
||||
background:transparent url(../extjs/resources/images/default/menu/unchecked.gif) no-repeat 0 0;
|
||||
}
|
||||
.xcheckbox-disabled {
|
||||
opacity: 0.5;
|
||||
-moz-opacity: 0.5;
|
||||
filter: alpha(opacity=50);
|
||||
cursor:default;
|
||||
}
|
141
src/webui/static/xcheckbox/xcheckbox.js
Normal file
141
src/webui/static/xcheckbox/xcheckbox.js
Normal file
|
@ -0,0 +1,141 @@
|
|||
// vim: ts=4:sw=4:nu:fdc=2:nospell
|
||||
/**
|
||||
* Ext.ux.form.XCheckbox - nice checkbox with configurable submit values
|
||||
*
|
||||
* @author Ing. Jozef Sakalos
|
||||
* @version $Id: Ext.ux.form.XCheckbox.js 81 2008-03-20 11:13:36Z jozo $
|
||||
* @date 10. February 2008
|
||||
*
|
||||
*
|
||||
* @license Ext.ux.form.XCheckbox is licensed under the terms of
|
||||
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
|
||||
* that the code/component(s) do NOT become part of another Open Source or Commercially
|
||||
* licensed development library or toolkit without explicit permission.
|
||||
*
|
||||
* License details: http://www.gnu.org/licenses/lgpl.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default css:
|
||||
* .xcheckbox-wrap {
|
||||
* line-height: 18px;
|
||||
* padding-top:2px;
|
||||
* }
|
||||
* .xcheckbox-wrap a {
|
||||
* display:block;
|
||||
* width:16px;
|
||||
* height:16px;
|
||||
* float:left;
|
||||
* }
|
||||
* .x-toolbar .xcheckbox-wrap {
|
||||
* padding: 0 0 2px 0;
|
||||
* }
|
||||
* .xcheckbox-on {
|
||||
* background:transparent url(./ext/resources/images/default/menu/checked.gif) no-repeat 0 0;
|
||||
* }
|
||||
* .xcheckbox-off {
|
||||
* background:transparent url(./ext/resources/images/default/menu/unchecked.gif) no-repeat 0 0;
|
||||
* }
|
||||
* .xcheckbox-disabled {
|
||||
* opacity: 0.5;
|
||||
* -moz-opacity: 0.5;
|
||||
* filter: alpha(opacity=50);
|
||||
* cursor:default;
|
||||
* }
|
||||
*
|
||||
* @class Ext.ux.XCheckbox
|
||||
* @extends Ext.form.Checkbox
|
||||
*/
|
||||
Ext.ns('Ext.ux.form');
|
||||
Ext.ux.form.XCheckbox = Ext.extend(Ext.form.Checkbox, {
|
||||
offCls:'xcheckbox-off'
|
||||
,onCls:'xcheckbox-on'
|
||||
,disabledClass:'xcheckbox-disabled'
|
||||
,submitOffValue:'false'
|
||||
,submitOnValue:'true'
|
||||
,checked:false
|
||||
|
||||
,onRender:function(ct) {
|
||||
// call parent
|
||||
Ext.ux.form.XCheckbox.superclass.onRender.apply(this, arguments);
|
||||
|
||||
// save tabIndex remove & re-create this.el
|
||||
var tabIndex = this.el.dom.tabIndex;
|
||||
var id = this.el.dom.id;
|
||||
this.el.remove();
|
||||
this.el = ct.createChild({tag:'input', type:'hidden', name:this.name, id:id});
|
||||
|
||||
// update value of hidden field
|
||||
this.updateHidden();
|
||||
|
||||
// adjust wrap class and create link with bg image to click on
|
||||
this.wrap.replaceClass('x-form-check-wrap', 'xcheckbox-wrap');
|
||||
this.cbEl = this.wrap.createChild({tag:'a', href:'#', cls:this.checked ? this.onCls : this.offCls});
|
||||
|
||||
// reposition boxLabel if any
|
||||
var boxLabel = this.wrap.down('label');
|
||||
if(boxLabel) {
|
||||
this.wrap.appendChild(boxLabel);
|
||||
}
|
||||
|
||||
// support tooltip
|
||||
if(this.tooltip) {
|
||||
this.cbEl.set({qtip:this.tooltip});
|
||||
}
|
||||
|
||||
// install event handlers
|
||||
this.wrap.on({click:{scope:this, fn:this.onClick, delegate:'a'}});
|
||||
this.wrap.on({keyup:{scope:this, fn:this.onClick, delegate:'a'}});
|
||||
|
||||
// restore tabIndex
|
||||
this.cbEl.dom.tabIndex = tabIndex;
|
||||
} // eo function onRender
|
||||
|
||||
,onClick:function(e) {
|
||||
if(this.disabled || this.readOnly) {
|
||||
return;
|
||||
}
|
||||
if(!e.isNavKeyPress()) {
|
||||
this.setValue(!this.checked);
|
||||
}
|
||||
} // eo function onClick
|
||||
|
||||
,onDisable:function() {
|
||||
this.cbEl.addClass(this.disabledClass);
|
||||
this.el.dom.disabled = true;
|
||||
} // eo function onDisable
|
||||
|
||||
,onEnable:function() {
|
||||
this.cbEl.removeClass(this.disabledClass);
|
||||
this.el.dom.disabled = false;
|
||||
} // eo function onEnable
|
||||
|
||||
,setValue:function(val) {
|
||||
if('string' == typeof val) {
|
||||
this.checked = val === this.submitOnValue;
|
||||
}
|
||||
else {
|
||||
this.checked = !(!val);
|
||||
}
|
||||
|
||||
if(this.rendered && this.cbEl) {
|
||||
this.updateHidden();
|
||||
this.cbEl.removeClass([this.offCls, this.onCls]);
|
||||
this.cbEl.addClass(this.checked ? this.onCls : this.offCls);
|
||||
}
|
||||
this.fireEvent('check', this, this.checked);
|
||||
|
||||
} // eo function setValue
|
||||
|
||||
,updateHidden:function() {
|
||||
this.el.dom.value = this.checked ? this.submitOnValue : this.submitOffValue;
|
||||
} // eo function updateHidden
|
||||
|
||||
,getValue:function() {
|
||||
return this.checked;
|
||||
} // eo function getValue
|
||||
|
||||
}); // eo extend
|
||||
|
||||
// register xtype
|
||||
Ext.reg('xcheckbox', Ext.ux.form.XCheckbox);
|
|
@ -1044,7 +1044,13 @@ page_imagecache(http_connection_t *hc, const char *remain, void *opaque)
|
|||
if(sscanf(remain, "%d", &id) != 1)
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
if ((fd = imagecache_open(id)) < 0)
|
||||
/* Fetch details */
|
||||
pthread_mutex_lock(&global_lock);
|
||||
fd = imagecache_open(id);
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
||||
/* Check result */
|
||||
if (fd < 0)
|
||||
return 404;
|
||||
if (fstat(fd, &st)) {
|
||||
close(fd);
|
||||
|
|
Loading…
Add table
Reference in a new issue