Shortcodes Ultimate - Version 3.7.0

Version Description

Upgrade normally via your Wordpress admin -> Plugins panel.

Download this release

Release Info

Developer gn_themes
Plugin Icon 128x128 Shortcodes Ultimate
Version 3.7.0
Comparing to
See all releases

Code changes from version 3.6.1 to 3.7.0

css/admin.css CHANGED
@@ -105,6 +105,9 @@
105
border-bottom: 1px solid #DFDFDF;
106
border-left: 1px solid #DFDFDF;
107
}
108
109
.no-js .su-pane {
110
display: block;
105
border-bottom: 1px solid #DFDFDF;
106
border-left: 1px solid #DFDFDF;
107
}
108
+ .su-pane table.fixed input[type="checkbox"] {
109
+ margin: 0 5px 1px 0;
110
+ }
111
112
.no-js .su-pane {
113
display: block;
css/generator.css CHANGED
@@ -25,7 +25,6 @@
25
26
#su-generator-select {
27
width: 400px;
28
- padding: 5px;
29
}
30
31
#su-generator-tools {
25
26
#su-generator-select {
27
width: 400px;
28
}
29
30
#su-generator-tools {
css/style.css CHANGED
@@ -145,19 +145,22 @@
145
146
/* Spoiler */
147
.su-spoiler-style-1 { margin: 0 0 1.5em 0 }
148
- .su-spoiler-style-1 .su-spoiler-title {
149
padding: 0.3em 0 0.3em 26px;
150
font-weight: bold;
151
background: 0 50% url(../images/spoiler-closed.png) no-repeat;
152
cursor: pointer;
153
}
154
- .su-spoiler-style-1.su-spoiler-open .su-spoiler-title {
155
background: 0 50% url(../images/spoiler-open.png) no-repeat;
156
}
157
.su-spoiler-style-1 .su-spoiler-content {
158
padding: 15px 0;
159
display: none;
160
}
161
162
.su-spoiler-style-2 {
163
margin-bottom: 1em;
@@ -167,7 +170,7 @@
167
-moz-border-radius: 10px;
168
-webkit-border-radius: 10px;
169
}
170
- .su-spoiler-style-2 .su-spoiler-title {
171
padding: 10px 10px 10px 40px;
172
background: #f0f0f0 12px 8px url(../images/spoiler-style-2.png) no-repeat;
173
font-size: 14px;
@@ -180,7 +183,7 @@
180
-moz-border-radius: 10px;
181
-webkit-border-radius: 10px;
182
}
183
- .su-spoiler-style-2.su-spoiler-open .su-spoiler-title {
184
border-bottom: 1px solid #ccc;
185
background: #f0f0f0 12px -92px url(../images/spoiler-style-2.png) no-repeat;
186
}
@@ -343,6 +346,26 @@
343
padding: 1px 0;
344
}
345
346
/* Button */
347
.su-button {
348
display: inline-block;
@@ -359,32 +382,28 @@
359
padding: 0 !important;
360
}
361
362
- .su-button-style-1 span {
363
- background: 0 -20px url(../images/button-style-1.png) repeat-x;
364
- }
365
- .su-button-style-1:hover span {
366
- background: 0 0 url(../images/button-style-1.png) repeat-x;
367
- }
368
369
- .su-button-style-2 span {
370
- background: 0 50% url(../images/button-style-2.png) repeat-x;
371
- }
372
- .su-button-style-2:hover span {
373
- background: 100% 50% url(../images/button-style-2.png) repeat-x;
374
- }
375
376
- .su-button-style-3 span {
377
- background: 0 50% url(../images/button-style-3.png) no-repeat;
378
- }
379
- .su-button-style-3:hover span {
380
- background: -710px 50% url(../images/button-style-3.png) no-repeat;
381
- }
382
383
- .su-button-style-4 span {
384
- background: 0 0 url(../images/button-style-4.png) repeat-x;
385
}
386
- .su-button-style-4:hover span {
387
- background: 0 0 url(../images/button-style-1.png) repeat-x;
388
}
389
390
/* Fancy link */
145
146
/* Spoiler */
147
.su-spoiler-style-1 { margin: 0 0 1.5em 0 }
148
+ .su-spoiler-style-1 > .su-spoiler-title {
149
padding: 0.3em 0 0.3em 26px;
150
font-weight: bold;
151
background: 0 50% url(../images/spoiler-closed.png) no-repeat;
152
cursor: pointer;
153
}
154
+ .su-spoiler-style-1.su-spoiler-open > .su-spoiler-title {
155
background: 0 50% url(../images/spoiler-open.png) no-repeat;
156
}
157
.su-spoiler-style-1 .su-spoiler-content {
158
padding: 15px 0;
159
display: none;
160
}
161
+ .su-spoiler-style-1 .su-spoiler-style-1 {
162
+ margin: 1em;
163
+ }
164
165
.su-spoiler-style-2 {
166
margin-bottom: 1em;
170
-moz-border-radius: 10px;
171
-webkit-border-radius: 10px;
172
}
173
+ .su-spoiler-style-2 > .su-spoiler-title {
174
padding: 10px 10px 10px 40px;
175
background: #f0f0f0 12px 8px url(../images/spoiler-style-2.png) no-repeat;
176
font-size: 14px;
183
-moz-border-radius: 10px;
184
-webkit-border-radius: 10px;
185
}
186
+ .su-spoiler-style-2.su-spoiler-open > .su-spoiler-title {
187
border-bottom: 1px solid #ccc;
188
background: #f0f0f0 12px -92px url(../images/spoiler-style-2.png) no-repeat;
189
}
346
padding: 1px 0;
347
}
348
349
+ /* Label */
350
+ .su-label {
351
+ display: inline-block;
352
+ margin-right: 0.5em;
353
+ padding: 0.2em 0.3em;
354
+ font-size: 0.8em;
355
+ height: 0.8em;
356
+ line-height: 0.8em;
357
+ text-transform: uppercase;
358
+ color: #fff;
359
+ border-radius: 3px;
360
+ -moz-border-radius: 3px;
361
+ -webkit-border-radius: 3px;
362
+ }
363
+ .su-label-style-default { background: #999 }
364
+ .su-label-style-success { background: #090 }
365
+ .su-label-style-warning { background: #f90 }
366
+ .su-label-style-important { background: #f03 }
367
+ .su-label-style-info { background: #09c }
368
+
369
/* Button */
370
.su-button {
371
display: inline-block;
382
padding: 0 !important;
383
}
384
385
+ .su-button-style-1 span { background: 0 -20px url(../images/button-style-1.png) repeat-x }
386
+ .su-button-style-1:hover span { background: 0 0 url(../images/button-style-1.png) repeat-x }
387
388
+ .su-button-style-2 span { background: 0 50% url(../images/button-style-2.png) repeat-x }
389
+ .su-button-style-2:hover span { background: 100% 50% url(../images/button-style-2.png) repeat-x }
390
391
+ .su-button-style-3 span { background: 0 50% url(../images/button-style-3.png) no-repeat }
392
+ .su-button-style-3:hover span { background: -710px 50% url(../images/button-style-3.png) no-repeat }
393
+
394
+ .su-button-style-4 span { background: 0 0 url(../images/button-style-4.png) repeat-x }
395
+ .su-button-style-4:hover span { background: 0 0 url(../images/button-style-1.png) repeat-x }
396
397
+ .su-button-style-5 span {
398
+ margin: 1px;
399
+ border: 1px dashed #fff !important;
400
}
401
+ .su-button-style-5:hover span {
402
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
403
+ filter: alpha(opacity=70);
404
+ -moz-opacity: 0.7;
405
+ -khtml-opacity: 0.7;
406
+ opacity: 0.7;
407
}
408
409
/* Fancy link */
images/demo/label.png ADDED
Binary file
languages/shortcodes-ultimate-ru_RU.mo CHANGED
Binary file
languages/shortcodes-ultimate-ru_RU.po CHANGED
@@ -2,8 +2,8 @@ msgid ""
2
msgstr ""
3
"Project-Id-Version: gn_themes\n"
4
"Report-Msgid-Bugs-To: \n"
5
- "POT-Creation-Date: 2012-01-08 00:27+0300\n"
6
- "PO-Revision-Date: 2012-01-08 00:27+0300\n"
7
"Last-Translator: Vladimir Anokhin <ano.vladimir@gmail.com>\n"
8
"Language-Team: \n"
9
"MIME-Version: 1.0\n"
@@ -28,35 +28,35 @@ msgstr "Владимир Анохин"
28
msgid "Provides support for many easy to use shortcodes"
29
msgstr "Предоставляет поддержку множества полезных шорткодов"
30
31
- #: shortcodes-ultimate.php:270
32
- #: shortcodes-ultimate.php:321
33
#: lib/admin.php:59
34
msgid "Settings"
35
msgstr "Настройки"
36
37
- #: shortcodes-ultimate.php:283
38
#: lib/admin.php:85
39
msgid "Settings saved"
40
msgstr "Настройки сохранены"
41
42
- #: shortcodes-ultimate.php:288
43
#: lib/admin.php:146
44
msgid "Custom CSS saved"
45
msgstr "Произвольные стили сохранены"
46
47
- #: shortcodes-ultimate.php:296
48
msgid "Insert shortcode"
49
msgstr "Вставка шорткода"
50
51
- #: shortcodes-ultimate.php:311
52
msgid "Select shortcode"
53
msgstr "Выберите шорткод"
54
55
- #: shortcodes-ultimate.php:322
56
msgid "Color picker"
57
msgstr "Подбор цвета"
58
59
- #: shortcodes-ultimate.php:323
60
#: lib/admin.php:68
61
msgid "Support forum"
62
msgstr "Форум поддержки"
@@ -326,388 +326,412 @@ msgstr "Цвет текста"
326
msgid "Highlighted text"
327
msgstr "Подсвеченный текст"
328
329
- #: lib/available.php:225
330
msgid "Dropcap style"
331
msgstr "Стиль буквицы"
332
333
- #: lib/available.php:236
334
msgid "Dropcap size"
335
msgstr "Размер буквицы"
336
337
- #: lib/available.php:240
338
msgid "D"
339
msgstr "D"
340
341
- #: lib/available.php:241
342
msgid "Dropcap"
343
msgstr "Буквица"
344
345
- #: lib/available.php:264
346
msgid "Option name"
347
msgstr "Имя опции"
348
349
- #: lib/available.php:268
350
msgid "Blog info"
351
msgstr "Инфо блога"
352
353
- #: lib/available.php:278
354
msgid "Post/page ID"
355
msgstr "ID записи/страницы"
356
357
- #: lib/available.php:286
358
msgid "Link target"
359
msgstr "Цель ссылки"
360
361
- #: lib/available.php:290
362
msgid "Permalink text"
363
msgstr "Текст постоянной ссылки"
364
365
- #: lib/available.php:291
366
msgid "Permalink to specified post/page"
367
msgstr "Постоянная ссылка на указанную запись/страницу"
368
369
- #: lib/available.php:301
370
msgid "Button link"
371
msgstr "Ссылка кнопки"
372
373
- #: lib/available.php:306
374
msgid "Button background color"
375
msgstr "Цвет фона кнопки"
376
377
- #: lib/available.php:325
378
msgid "Button size"
379
msgstr "Размер кнопки"
380
381
- #: lib/available.php:335
382
msgid "Button background style"
383
msgstr "Стиль фона кнопки"
384
385
- #: lib/available.php:343
386
msgid "Dark text color"
387
msgstr "Темный цвет текста"
388
389
- #: lib/available.php:351
390
- msgid "Disable rounded corners"
391
- msgstr "Отключить скругленные уголки"
392
393
- #: lib/available.php:356
394
msgid "Button icon"
395
msgstr "Иконка кнопки"
396
397
- #: lib/available.php:361
398
msgid "Button class"
399
msgstr "Класс кнопки"
400
401
- #: lib/available.php:369
402
msgid "Button link target"
403
msgstr "Цель ссылки кнопки"
404
405
- #: lib/available.php:373
406
msgid "Button text"
407
msgstr "Текст кнопки"
408
409
- #: lib/available.php:374
410
msgid "Styled button"
411
msgstr "Стильная кнопка"
412
413
- #: lib/available.php:387
414
msgid "Link color"
415
msgstr "Цвет ссылки"
416
417
- #: lib/available.php:392
418
msgid "URL"
419
msgstr "URL"
420
421
- #: lib/available.php:396
422
msgid "Link text"
423
msgstr "Текст ссылки"
424
425
- #: lib/available.php:397
426
msgid "Fancy link"
427
msgstr "Стильная ссылка"
428
429
- #: lib/available.php:406
430
- #: lib/available.php:407
431
msgid "Service title"
432
msgstr "Название услуги"
433
434
- #: lib/available.php:412
435
msgid "Service icon"
436
msgstr "Иконка услуги"
437
438
- #: lib/available.php:421
439
msgid "Icon size"
440
msgstr "Размер иконки"
441
442
- #: lib/available.php:425
443
msgid "Service description"
444
msgstr "Описание услуги"
445
446
- #: lib/available.php:426
447
msgid "Service box with title"
448
msgstr "Блок услуга с заголовком"
449
450
- #: lib/available.php:440
451
msgid "Box style"
452
msgstr "Стиль блока"
453
454
- #: lib/available.php:448
455
msgid "Show login message"
456
msgstr "Показывать предложение войти"
457
458
- #: lib/available.php:452
459
msgid "Content for logged members"
460
msgstr "Содержимое для залогиненых пользователей"
461
462
- #: lib/available.php:453
463
msgid "Content for logged in members only"
464
msgstr "Ссодержимое только для залогиненных"
465
466
- #: lib/available.php:461
467
msgid "Content for guests"
468
msgstr "Содержимое для гостей"
469
470
- #: lib/available.php:462
471
msgid "Content for guests only"
472
msgstr "Ссодержимое только для гостей"
473
474
- #: lib/available.php:471
475
- #: lib/available.php:472
476
msgid "Box title"
477
msgstr "Заголовок блока"
478
479
- #: lib/available.php:477
480
msgid "Box color"
481
msgstr "Цвет блока"
482
483
- #: lib/available.php:482
484
msgid "Box content"
485
msgstr "Содержимое блока"
486
487
- #: lib/available.php:483
488
msgid "Colored box with caption"
489
msgstr "Цветной блок с заголовком"
490
491
- #: lib/available.php:493
492
msgid "Note color"
493
msgstr "Цвет блока"
494
495
- #: lib/available.php:498
496
msgid "Note text"
497
msgstr "Текст заметки"
498
499
- #: lib/available.php:499
500
msgid "Colored box"
501
msgstr "Цветной блок"
502
503
- #: lib/available.php:507
504
msgid "Private note text"
505
msgstr "Текст приватной заметки"
506
507
- #: lib/available.php:508
508
msgid "Private note for post authors"
509
msgstr "Приватный текст для других авторов"
510
511
- #: lib/available.php:534
512
msgid "List style"
513
msgstr "Стиль списка"
514
515
- #: lib/available.php:538
516
msgid "List item "
517
msgstr "Элемент списка"
518
519
- #: lib/available.php:539
520
msgid "Styled unordered list"
521
msgstr "Стильный неупорядоченный список"
522
523
- #: lib/available.php:549
524
msgid "Feed URL"
525
msgstr "URL ленты"
526
527
- #: lib/available.php:560
528
msgid "Number of item to show"
529
msgstr "Количество элементов для показа"
530
531
- #: lib/available.php:564
532
msgid "Feed grabber"
533
msgstr "Граббер новостных лент"
534
535
- #: lib/available.php:574
536
msgid "Custom menu name"
537
msgstr "Имя произвольного меню"
538
539
- #: lib/available.php:578
540
msgid "Custom menu by name"
541
msgstr "Произвольное меню"
542
543
- #: lib/available.php:592
544
- #: lib/available.php:615
545
msgid "Depth level"
546
msgstr "Глубина"
547
548
- #: lib/available.php:597
549
msgid "Parent page ID"
550
msgstr "ID родительской страницы"
551
552
- #: lib/available.php:601
553
msgid "Page childrens"
554
msgstr "Потомки страницы"
555
556
- #: lib/available.php:619
557
msgid "Page siblings"
558
msgstr "Братья страницы"
559
560
- #: lib/available.php:641
561
msgid "Column width"
562
msgstr "Ширина колонки"
563
564
- #: lib/available.php:649
565
msgid "Last column"
566
msgstr "Последняя колонка"
567
568
- #: lib/available.php:658
569
msgid "Column style"
570
msgstr "Стиль колонки"
571
572
- #: lib/available.php:662
573
msgid "Column content"
574
msgstr "Содержимое колонки"
575
576
- #: lib/available.php:663
577
msgid "Flexible columns"
578
msgstr "Резновые колонки"
579
580
- #: lib/available.php:677
581
msgid "Table style"
582
msgstr "Стиль таблицы"
583
584
- #: lib/available.php:682
585
msgid "Create table from CSV"
586
msgstr "Создание таблицы из CSV файла"
587
588
- #: lib/available.php:687
589
msgid "Styled table from HTML or CSV file"
590
msgstr "Стильная таблица из HTML или CSV файла"
591
592
- #: lib/available.php:763
593
msgid "Media URL"
594
msgstr "Ссылка на медиа"
595
596
- #: lib/available.php:768
597
- #: lib/available.php:792
598
- #: lib/available.php:811
599
msgid "Width"
600
msgstr "Ширина"
601
602
- #: lib/available.php:773
603
- #: lib/available.php:797
604
- #: lib/available.php:816
605
msgid "Height"
606
msgstr "Высота"
607
608
- #: lib/available.php:777
609
msgid "YouTube video, Vimeo video, .mp4/.flv video, .mp3 file or images"
610
msgstr "YouTube видео, Vimeo видео, .mp4/.flv видео, .mp3 файл или изображения"
611
612
- #: lib/available.php:787
613
msgid "Document URL"
614
msgstr "Ссылка на документ"
615
616
- #: lib/available.php:801
617
msgid ".doc, .xls, .pdf viewer by Google"
618
msgstr ".doc, .xls, .pdf просмотрщик от Google"
619
620
- #: lib/available.php:821
621
msgid "Marker address"
622
msgstr "Адрес маркера"
623
624
- #: lib/available.php:825
625
msgid "Maps by Google"
626
msgstr "Карты от Google"
627
628
- #: lib/available.php:839
629
- #: lib/available.php:909
630
- #: lib/available.php:981
631
msgid "Source of images"
632
msgstr "Источник изображений"
633
634
- #: lib/available.php:850
635
- #: lib/available.php:920
636
- #: lib/available.php:992
637
msgid "Images links"
638
msgstr "Ссылки с изображений"
639
640
- #: lib/available.php:861
641
msgid "Slider size"
642
msgstr "Размер слайдера"
643
644
- #: lib/available.php:871
645
msgid "Number of slides"
646
msgstr "Количество слайдов"
647
648
- #: lib/available.php:881
649
msgid "Animation effect"
650
msgstr "Эффект анимации"
651
652
- #: lib/available.php:886
653
- #: lib/available.php:954
654
msgid "Animation speed (1000 = 1 second)"
655
msgstr "Скорость анимации (1000 = 1 секунда)"
656
657
- #: lib/available.php:891
658
msgid "Animation delay (1000 = 1 second)"
659
msgstr "Задержка анимации (1000 = 1 секунда)"
660
661
- #: lib/available.php:895
662
msgid "Nivo slider by attached to post images"
663
msgstr "Nivo slider из изображений записи"
664
665
- #: lib/available.php:930
666
msgid "Carousel item size"
667
msgstr "Размер элементов карусели"
668
669
- #: lib/available.php:940
670
- #: lib/available.php:1012
671
msgid "Number of items"
672
msgstr "Количество элементов"
673
674
- #: lib/available.php:949
675
msgid "Number of items in viewport"
676
msgstr "Количество элементов в видмой части"
677
678
- #: lib/available.php:963
679
msgid "Space between items in pixels"
680
msgstr "Расстояние между элементами в пикселях"
681
682
- #: lib/available.php:967
683
msgid "jCarousel by attached to post images"
684
msgstr "jCarousel из изображений записи"
685
686
- #: lib/available.php:1002
687
msgid "Gallery item size"
688
msgstr "Размер миниатюр"
689
690
- #: lib/available.php:1016
691
msgid "Custom gallery by attached to post images"
692
msgstr "Произвольная галерея"
693
694
- #: lib/available.php:1026
695
msgid "Twitter username"
696
msgstr "Имя пользователя"
697
698
- #: lib/available.php:1037
699
msgid "Number of tweets to show"
700
msgstr "Количество твитов для показа"
701
702
- #: lib/available.php:1045
703
msgid "Tweets style"
704
msgstr "Стиль твитов"
705
706
- #: lib/available.php:1053
707
msgid "Show relative time"
708
msgstr "Показывать время"
709
710
- #: lib/available.php:1057
711
msgid "Recent tweets"
712
msgstr "Последние твиты"
713
@@ -723,33 +747,33 @@ msgstr "Вставить"
723
msgid "Top"
724
msgstr "Вверх"
725
726
- #: lib/shortcodes.php:339
727
msgid "Service name"
728
msgstr "Название услуги"
729
730
- #: lib/shortcodes.php:357
731
msgid "This is box title"
732
msgstr "Заголовок блока"
733
734
- #: lib/shortcodes.php:419
735
msgid "Please specify media url"
736
msgstr "Укажите ссылку на медиа"
737
738
- #: lib/shortcodes.php:558
739
- #: lib/shortcodes.php:611
740
- #: lib/shortcodes.php:655
741
msgid "no attached images, or only one attached image"
742
msgstr "не найдены загруженные изображения, или изображение всего одно"
743
744
- #: lib/shortcodes.php:770
745
msgid "This menu doesn't exists, or has no elements"
746
msgstr "Такое меню не существует, или в нем нет ни одного элемента"
747
748
- #: lib/shortcodes.php:827
749
msgid "This content is for members only."
750
msgstr "Это содержимое только для участников."
751
752
- #: lib/shortcodes.php:827
753
msgid "Please login"
754
msgstr "Пожалуйста войдите"
755
@@ -790,6 +814,9 @@ msgstr "имя пользователя не задано"
790
msgid "no public messages"
791
msgstr "нет публичных твитов"
792
793
#~ msgid "Bug report"
794
#~ msgstr "Сообщить об ошибке"
795
2
msgstr ""
3
"Project-Id-Version: gn_themes\n"
4
"Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: 2012-02-08 09:59+0300\n"
6
+ "PO-Revision-Date: 2012-02-08 10:01+0300\n"
7
"Last-Translator: Vladimir Anokhin <ano.vladimir@gmail.com>\n"
8
"Language-Team: \n"
9
"MIME-Version: 1.0\n"
28
msgid "Provides support for many easy to use shortcodes"
29
msgstr "Предоставляет поддержку множества полезных шорткодов"
30
31
+ #: shortcodes-ultimate.php:280
32
+ #: shortcodes-ultimate.php:331
33
#: lib/admin.php:59
34
msgid "Settings"
35
msgstr "Настройки"
36
37
+ #: shortcodes-ultimate.php:293
38
#: lib/admin.php:85
39
msgid "Settings saved"
40
msgstr "Настройки сохранены"
41
42
+ #: shortcodes-ultimate.php:298
43
#: lib/admin.php:146
44
msgid "Custom CSS saved"
45
msgstr "Произвольные стили сохранены"
46
47
+ #: shortcodes-ultimate.php:306
48
msgid "Insert shortcode"
49
msgstr "Вставка шорткода"
50
51
+ #: shortcodes-ultimate.php:321
52
msgid "Select shortcode"
53
msgstr "Выберите шорткод"
54
55
+ #: shortcodes-ultimate.php:332
56
msgid "Color picker"
57
msgstr "Подбор цвета"
58
59
+ #: shortcodes-ultimate.php:333
60
#: lib/admin.php:68
61
msgid "Support forum"
62
msgstr "Форум поддержки"
326
msgid "Highlighted text"
327
msgstr "Подсвеченный текст"
328
329
+ #: lib/available.php:227
330
+ msgid "Label style"
331
+ msgstr "Стиль лейбла"
332
+
333
+ #: lib/available.php:231
334
+ msgid "Label"
335
+ msgstr "Лейбл"
336
+
337
+ #: lib/available.php:232
338
+ msgid "Styled label"
339
+ msgstr "Стильный лейбл"
340
+
341
+ #: lib/available.php:246
342
msgid "Dropcap style"
343
msgstr "Стиль буквицы"
344
345
+ #: lib/available.php:257
346
msgid "Dropcap size"
347
msgstr "Размер буквицы"
348
349
+ #: lib/available.php:261
350
msgid "D"
351
msgstr "D"
352
353
+ #: lib/available.php:262
354
msgid "Dropcap"
355
msgstr "Буквица"
356
357
+ #: lib/available.php:285
358
msgid "Option name"
359
msgstr "Имя опции"
360
361
+ #: lib/available.php:289
362
msgid "Blog info"
363
msgstr "Инфо блога"
364
365
+ #: lib/available.php:299
366
msgid "Post/page ID"
367
msgstr "ID записи/страницы"
368
369
+ #: lib/available.php:307
370
msgid "Link target"
371
msgstr "Цель ссылки"
372
373
+ #: lib/available.php:311
374
msgid "Permalink text"
375
msgstr "Текст постоянной ссылки"
376
377
+ #: lib/available.php:312
378
msgid "Permalink to specified post/page"
379
msgstr "Постоянная ссылка на указанную запись/страницу"
380
381
+ #: lib/available.php:322
382
msgid "Button link"
383
msgstr "Ссылка кнопки"
384
385
+ #: lib/available.php:327
386
msgid "Button background color"
387
msgstr "Цвет фона кнопки"
388
389
+ #: lib/available.php:346
390
msgid "Button size"
391
msgstr "Размер кнопки"
392
393
+ #: lib/available.php:357
394
msgid "Button background style"
395
msgstr "Стиль фона кнопки"
396
397
+ #: lib/available.php:365
398
msgid "Dark text color"
399
msgstr "Темный цвет текста"
400
401
+ #: lib/available.php:376
402
+ msgid "Corners radius"
403
+ msgstr "Радиус углов"
404
405
+ #: lib/available.php:381
406
msgid "Button icon"
407
msgstr "Иконка кнопки"
408
409
+ #: lib/available.php:386
410
msgid "Button class"
411
msgstr "Класс кнопки"
412
413
+ #: lib/available.php:394
414
msgid "Button link target"
415
msgstr "Цель ссылки кнопки"
416
417
+ #: lib/available.php:398
418
msgid "Button text"
419
msgstr "Текст кнопки"
420
421
+ #: lib/available.php:399
422
msgid "Styled button"
423
msgstr "Стильная кнопка"
424
425
+ #: lib/available.php:412
426
msgid "Link color"
427
msgstr "Цвет ссылки"
428
429
+ #: lib/available.php:417
430
msgid "URL"
431
msgstr "URL"
432
433
+ #: lib/available.php:421
434
msgid "Link text"
435
msgstr "Текст ссылки"
436
437
+ #: lib/available.php:422
438
msgid "Fancy link"
439
msgstr "Стильная ссылка"
440
441
+ #: lib/available.php:431
442
+ #: lib/available.php:432
443
msgid "Service title"
444
msgstr "Название услуги"
445
446
+ #: lib/available.php:437
447
msgid "Service icon"
448
msgstr "Иконка услуги"
449
450
+ #: lib/available.php:446
451
msgid "Icon size"
452
msgstr "Размер иконки"
453
454
+ #: lib/available.php:450
455
msgid "Service description"
456
msgstr "Описание услуги"
457
458
+ #: lib/available.php:451
459
msgid "Service box with title"
460
msgstr "Блок услуга с заголовком"
461
462
+ #: lib/available.php:465
463
msgid "Box style"
464
msgstr "Стиль блока"
465
466
+ #: lib/available.php:473
467
msgid "Show login message"
468
msgstr "Показывать предложение войти"
469
470
+ #: lib/available.php:477
471
msgid "Content for logged members"
472
msgstr "Содержимое для залогиненых пользователей"
473
474
+ #: lib/available.php:478
475
msgid "Content for logged in members only"
476
msgstr "Ссодержимое только для залогиненных"
477
478
+ #: lib/available.php:486
479
msgid "Content for guests"
480
msgstr "Содержимое для гостей"
481
482
+ #: lib/available.php:487
483
msgid "Content for guests only"
484
msgstr "Ссодержимое только для гостей"
485
486
+ #: lib/available.php:496
487
+ #: lib/available.php:497
488
msgid "Box title"
489
msgstr "Заголовок блока"
490
491
+ #: lib/available.php:502
492
msgid "Box color"
493
msgstr "Цвет блока"
494
495
+ #: lib/available.php:507
496
msgid "Box content"
497
msgstr "Содержимое блока"
498
499
+ #: lib/available.php:508
500
msgid "Colored box with caption"
501
msgstr "Цветной блок с заголовком"
502
503
+ #: lib/available.php:518
504
msgid "Note color"
505
msgstr "Цвет блока"
506
507
+ #: lib/available.php:523
508
msgid "Note text"
509
msgstr "Текст заметки"
510
511
+ #: lib/available.php:524
512
msgid "Colored box"
513
msgstr "Цветной блок"
514
515
+ #: lib/available.php:532
516
msgid "Private note text"
517
msgstr "Текст приватной заметки"
518
519
+ #: lib/available.php:533
520
msgid "Private note for post authors"
521
msgstr "Приватный текст для других авторов"
522
523
+ #: lib/available.php:559
524
msgid "List style"
525
msgstr "Стиль списка"
526
527
+ #: lib/available.php:563
528
msgid "List item "
529
msgstr "Элемент списка"
530
531
+ #: lib/available.php:564
532
msgid "Styled unordered list"
533
msgstr "Стильный неупорядоченный список"
534
535
+ #: lib/available.php:574
536
msgid "Feed URL"
537
msgstr "URL ленты"
538
539
+ #: lib/available.php:585
540
msgid "Number of item to show"
541
msgstr "Количество элементов для показа"
542
543
+ #: lib/available.php:589
544
msgid "Feed grabber"
545
msgstr "Граббер новостных лент"
546
547
+ #: lib/available.php:599
548
msgid "Custom menu name"
549
msgstr "Имя произвольного меню"
550
551
+ #: lib/available.php:603
552
msgid "Custom menu by name"
553
msgstr "Произвольное меню"
554
555
+ #: lib/available.php:617
556
+ #: lib/available.php:640
557
msgid "Depth level"
558
msgstr "Глубина"
559
560
+ #: lib/available.php:622
561
msgid "Parent page ID"
562
msgstr "ID родительской страницы"
563
564
+ #: lib/available.php:626
565
msgid "Page childrens"
566
msgstr "Потомки страницы"
567
568
+ #: lib/available.php:644
569
msgid "Page siblings"
570
msgstr "Братья страницы"
571
572
+ #: lib/available.php:666
573
msgid "Column width"
574
msgstr "Ширина колонки"
575
576
+ #: lib/available.php:674
577
msgid "Last column"
578
msgstr "Последняя колонка"
579
580
+ #: lib/available.php:683
581
msgid "Column style"
582
msgstr "Стиль колонки"
583
584
+ #: lib/available.php:687
585
msgid "Column content"
586
msgstr "Содержимое колонки"
587
588
+ #: lib/available.php:688
589
msgid "Flexible columns"
590
msgstr "Резновые колонки"
591
592
+ #: lib/available.php:702
593
msgid "Table style"
594
msgstr "Стиль таблицы"
595
596
+ #: lib/available.php:707
597
msgid "Create table from CSV"
598
msgstr "Создание таблицы из CSV файла"
599
600
+ #: lib/available.php:712
601
msgid "Styled table from HTML or CSV file"
602
msgstr "Стильная таблица из HTML или CSV файла"
603
604
+ #: lib/available.php:788
605
msgid "Media URL"
606
msgstr "Ссылка на медиа"
607
608
+ #: lib/available.php:793
609
+ #: lib/available.php:826
610
+ #: lib/available.php:845
611
msgid "Width"
612
msgstr "Ширина"
613
614
+ #: lib/available.php:798
615
+ #: lib/available.php:831
616
+ #: lib/available.php:850
617
msgid "Height"
618
msgstr "Высота"
619
620
+ #: lib/available.php:807
621
+ msgid "jwPlayer url-encoded params"
622
+ msgstr "Параметры для jwPlayer в урл-формате"
623
+
624
+ #: lib/available.php:811
625
msgid "YouTube video, Vimeo video, .mp4/.flv video, .mp3 file or images"
626
msgstr "YouTube видео, Vimeo видео, .mp4/.flv видео, .mp3 файл или изображения"
627
628
+ #: lib/available.php:821
629
msgid "Document URL"
630
msgstr "Ссылка на документ"
631
632
+ #: lib/available.php:835
633
msgid ".doc, .xls, .pdf viewer by Google"
634
msgstr ".doc, .xls, .pdf просмотрщик от Google"
635
636
+ #: lib/available.php:855
637
msgid "Marker address"
638
msgstr "Адрес маркера"
639
640
+ #: lib/available.php:859
641
msgid "Maps by Google"
642
msgstr "Карты от Google"
643
644
+ #: lib/available.php:873
645
+ #: lib/available.php:943
646
+ #: lib/available.php:1022
647
msgid "Source of images"
648
msgstr "Источник изображений"
649
650
+ #: lib/available.php:884
651
+ #: lib/available.php:954
652
+ #: lib/available.php:1033
653
msgid "Images links"
654
msgstr "Ссылки с изображений"
655
656
+ #: lib/available.php:895
657
msgid "Slider size"
658
msgstr "Размер слайдера"
659
660
+ #: lib/available.php:905
661
msgid "Number of slides"
662
msgstr "Количество слайдов"
663
664
+ #: lib/available.php:915
665
msgid "Animation effect"
666
msgstr "Эффект анимации"
667
668
+ #: lib/available.php:920
669
+ #: lib/available.php:988
670
msgid "Animation speed (1000 = 1 second)"
671
msgstr "Скорость анимации (1000 = 1 секунда)"
672
673
+ #: lib/available.php:925
674
msgid "Animation delay (1000 = 1 second)"
675
msgstr "Задержка анимации (1000 = 1 секунда)"
676
677
+ #: lib/available.php:929
678
msgid "Nivo slider by attached to post images"
679
msgstr "Nivo slider из изображений записи"
680
681
+ #: lib/available.php:964
682
msgid "Carousel item size"
683
msgstr "Размер элементов карусели"
684
685
+ #: lib/available.php:974
686
+ #: lib/available.php:1061
687
msgid "Number of items"
688
msgstr "Количество элементов"
689
690
+ #: lib/available.php:983
691
msgid "Number of items in viewport"
692
msgstr "Количество элементов в видмой части"
693
694
+ #: lib/available.php:997
695
msgid "Space between items in pixels"
696
msgstr "Расстояние между элементами в пикселях"
697
698
+ #: lib/available.php:1001
699
msgid "jCarousel by attached to post images"
700
msgstr "jCarousel из изображений записи"
701
702
+ #: lib/available.php:1013
703
+ msgid "Gallery style"
704
+ msgstr "Стиль галереи"
705
+
706
+ #: lib/available.php:1041
707
+ msgid "Show image description"
708
+ msgstr "Описания изображений"
709
+
710
+ #: lib/available.php:1051
711
msgid "Gallery item size"
712
msgstr "Размер миниатюр"
713
714
+ #: lib/available.php:1065
715
msgid "Custom gallery by attached to post images"
716
msgstr "Произвольная галерея"
717
718
+ #: lib/available.php:1075
719
msgid "Twitter username"
720
msgstr "Имя пользователя"
721
722
+ #: lib/available.php:1086
723
msgid "Number of tweets to show"
724
msgstr "Количество твитов для показа"
725
726
+ #: lib/available.php:1094
727
msgid "Tweets style"
728
msgstr "Стиль твитов"
729
730
+ #: lib/available.php:1102
731
msgid "Show relative time"
732
msgstr "Показывать время"
733
734
+ #: lib/available.php:1106
735
msgid "Recent tweets"
736
msgstr "Последние твиты"
737
747
msgid "Top"
748
msgstr "Вверх"
749
750
+ #: lib/shortcodes.php:358
751
msgid "Service name"
752
msgstr "Название услуги"
753
754
+ #: lib/shortcodes.php:376
755
msgid "This is box title"
756
msgstr "Заголовок блока"
757
758
+ #: lib/shortcodes.php:444
759
msgid "Please specify media url"
760
msgstr "Укажите ссылку на медиа"
761
762
+ #: lib/shortcodes.php:583
763
+ #: lib/shortcodes.php:636
764
+ #: lib/shortcodes.php:685
765
msgid "no attached images, or only one attached image"
766
msgstr "не найдены загруженные изображения, или изображение всего одно"
767
768
+ #: lib/shortcodes.php:800
769
msgid "This menu doesn't exists, or has no elements"
770
msgstr "Такое меню не существует, или в нем нет ни одного элемента"
771
772
+ #: lib/shortcodes.php:857
773
msgid "This content is for members only."
774
msgstr "Это содержимое только для участников."
775
776
+ #: lib/shortcodes.php:857
777
msgid "Please login"
778
msgstr "Пожалуйста войдите"
779
814
msgid "no public messages"
815
msgstr "нет публичных твитов"
816
817
+ #~ msgid "Disable rounded corners"
818
+ #~ msgstr "Отключить скругленные уголки"
819
+
820
#~ msgid "Bug report"
821
#~ msgstr "Сообщить об ошибке"
822
lib/available.php CHANGED
@@ -210,6 +210,27 @@
210
'content' => __( 'Highlighted text', 'shortcodes-ultimate' ),
211
'desc' => __( 'Highlighted text', 'shortcodes-ultimate' )
212
),
213
# dropcap
214
'dropcap' => array(
215
'name' => 'Dropcap',
@@ -329,7 +350,8 @@
329
'1',
330
'2',
331
'3',
332
- '4'
333
),
334
'default' => '1',
335
'desc' => __( 'Button background style', 'shortcodes-ultimate' )
@@ -342,13 +364,16 @@
342
'default' => '0',
343
'desc' => __( 'Dark text color', 'shortcodes-ultimate' )
344
),
345
- 'square' => array(
346
'values' => array(
347
'0',
348
- '1'
349
),
350
- 'default' => '0',
351
- 'desc' => __( 'Disable rounded corners', 'shortcodes-ultimate' )
352
),
353
'icon' => array(
354
'values' => array( ),
@@ -1077,7 +1102,7 @@
1077
'desc' => __( 'Show relative time', 'shortcodes-ultimate' )
1078
)
1079
),
1080
- 'usage' => '[tweets username="gn_themes" limit="3" style="1" format="teaser"]',
1081
'desc' => __( 'Recent tweets', 'shortcodes-ultimate' )
1082
),
1083
);
210
'content' => __( 'Highlighted text', 'shortcodes-ultimate' ),
211
'desc' => __( 'Highlighted text', 'shortcodes-ultimate' )
212
),
213
+ # label
214
+ 'label' => array(
215
+ 'name' => 'Label',
216
+ 'type' => 'wrap',
217
+ 'atts' => array(
218
+ 'style' => array(
219
+ 'values' => array(
220
+ 'default',
221
+ 'success',
222
+ 'warning',
223
+ 'important',
224
+ 'info'
225
+ ),
226
+ 'default' => 'default',
227
+ 'desc' => __( 'Label style', 'shortcodes-ultimate' )
228
+ )
229
+ ),
230
+ 'usage' => '[label style="info"]Something[/label]',
231
+ 'content' => __( 'Label', 'shortcodes-ultimate' ),
232
+ 'desc' => __( 'Styled label', 'shortcodes-ultimate' )
233
+ ),
234
# dropcap
235
'dropcap' => array(
236
'name' => 'Dropcap',
350
'1',
351
'2',
352
'3',
353
+ '4',
354
+ '5'
355
),
356
'default' => '1',
357
'desc' => __( 'Button background style', 'shortcodes-ultimate' )
364
'default' => '0',
365
'desc' => __( 'Dark text color', 'shortcodes-ultimate' )
366
),
367
+ 'radius' => array(
368
'values' => array(
369
+ 'auto',
370
'0',
371
+ '5',
372
+ '10',
373
+ '20'
374
),
375
+ 'default' => 'auto',
376
+ 'desc' => __( 'Corners radius', 'shortcodes-ultimate' )
377
),
378
'icon' => array(
379
'values' => array( ),
1102
'desc' => __( 'Show relative time', 'shortcodes-ultimate' )
1103
)
1104
),
1105
+ 'usage' => '[tweets username="gn_themes" limit="3" style="1" show_time="1"]',
1106
'desc' => __( 'Recent tweets', 'shortcodes-ultimate' )
1107
),
1108
);
lib/color.php CHANGED
@@ -45,16 +45,16 @@
45
if ( preg_match( '/^([0-9a-f])([0-9a-f])([0-9a-f])/i', $supplied_hex ) ) {
46
$supplied_hex = preg_replace( '/^([0-9a-f])([0-9a-f])([0-9a-f])/i', '\\1\\1\\2\\2\\3\\3', $supplied_hex );
47
} else {
48
- trigger_error( "Invalid hex value", E_USER_ERROR );
49
}
50
break;
51
case 6:
52
if ( !preg_match( '/^[0-9a-f]{2}[0-9a-f]{2}[0-9a-f]{2}#x2F;i', $supplied_hex ) ) {
53
- trigger_error( "Invalid hex value", E_USER_ERROR );
54
}
55
break;
56
default:
57
- trigger_error( "Invalid hex length", E_USER_ERROR );
58
}
59
60
// Start shifting
45
if ( preg_match( '/^([0-9a-f])([0-9a-f])([0-9a-f])/i', $supplied_hex ) ) {
46
$supplied_hex = preg_replace( '/^([0-9a-f])([0-9a-f])([0-9a-f])/i', '\\1\\1\\2\\2\\3\\3', $supplied_hex );
47
} else {
48
+ trigger_error( "Invalid hex color value", E_USER_ERROR );
49
}
50
break;
51
case 6:
52
if ( !preg_match( '/^[0-9a-f]{2}[0-9a-f]{2}[0-9a-f]{2}#x2F;i', $supplied_hex ) ) {
53
+ trigger_error( "Invalid hex color value", E_USER_ERROR );
54
}
55
break;
56
default:
57
+ trigger_error( "Invalid hex color length", E_USER_ERROR );
58
}
59
60
// Start shifting
lib/images.php CHANGED
@@ -34,6 +34,7 @@
34
'post_type' => 'attachment',
35
'numberposts' => $limit,
36
'order' => 'ASC',
37
'post_status' => null,
38
'post_parent' => $post_id
39
) );
34
'post_type' => 'attachment',
35
'numberposts' => $limit,
36
'order' => 'ASC',
37
+ 'orderby' => 'menu_order',
38
'post_status' => null,
39
'post_parent' => $post_id
40
) );
lib/shortcodes.php CHANGED
@@ -91,7 +91,7 @@
91
$open_class = ( $open ) ? ' su-spoiler-open' : '';
92
$open_display = ( $open ) ? ' style="display:block"' : '';
93
94
- return '<div class="su-spoiler su-spoiler-style-' . $style . $open_class . '"><div class="su-spoiler-title">' . $title . '</div><div class="su-spoiler-content"' . $open_display . '>' . su_do_shortcode( $content ) . '</div></div>';
95
}
96
97
/**
@@ -103,7 +103,7 @@
103
*/
104
function su_accordion_shortcode( $atts = null, $content = null ) {
105
106
- return '<div class="su-accordion">' . do_shortcode( $content ) . '</div>';
107
}
108
109
/**
@@ -152,6 +152,21 @@
152
return '<span class="su-highlight" style="background:' . $bg . ';color:' . $color . '">&nbsp;' . $content . '&nbsp;</span>';
153
}
154
155
/**
156
* Shortcode: dropcap
157
*
@@ -184,7 +199,7 @@
184
'style' => 0
185
), $atts ) );
186
187
- return ( $last ) ? '<div class="su-column su-column-' . $size . ' su-column-last su-column-style-' . $style . '">' . do_shortcode( $content ) . '</div><div class="su-spacer"></div>' : '<div class="su-column su-column-' . $size . ' su-column-style-' . $style . '">' . do_shortcode( $content ) . '</div>';
188
}
189
190
/**
@@ -245,6 +260,7 @@
245
'link' => '#',
246
'color' => '#aaa',
247
'dark' => false,
248
'square' => false,
249
'style' => 1,
250
'size' => 3,
@@ -253,8 +269,11 @@
253
'target' => false
254
), $atts ) );
255
256
$styles = array(
257
- 'border_radius' => ( $square ) ? 0 : round( $size + 2 ),
258
'dark_color' => su_hex_shift( $color, 'darker', 20 ),
259
'light_color' => su_hex_shift( $color, 'lighter', 70 ),
260
'size' => round( ( $size + 9 ) * 1.3 ),
@@ -363,7 +382,7 @@
363
'text_shadow' => su_hex_shift( $color, 'darker', 70 ),
364
);
365
366
- return '<div class="su-box" style="border:1px solid ' . $styles['dark_color'] . '"><div class="su-box-title" style="background-color:' . $color . ';border-top:1px solid ' . $styles['light_color'] . ';text-shadow:1px 1px 0 ' . $styles['text_shadow'] . '">' . $title . '</div><div class="su-box-content">' . do_shortcode( $content ) . '</div></div>';
367
}
368
369
/**
@@ -385,7 +404,7 @@
385
'text_color' => su_hex_shift( $color, 'darker', 70 )
386
);
387
388
- return '<div class="su-note" style="background-color:' . $styles['light_color'] . ';border:1px solid ' . $styles['dark_color'] . '"><div class="su-note-shell" style="border:1px solid ' . $styles['extra_light_color'] . ';color:' . $styles['text_color'] . '">' . do_shortcode( $content ) . '</div></div>';
389
}
390
391
/**
91
$open_class = ( $open ) ? ' su-spoiler-open' : '';
92
$open_display = ( $open ) ? ' style="display:block"' : '';
93
94
+ return '<div class="su-spoiler su-spoiler-style-' . $style . $open_class . '"><div class="su-spoiler-title">' . $title . '</div><div class="su-spoiler-content"' . $open_display . '>' . su_do_shortcode( $content, 's' ) . '</div></div>';
95
}
96
97
/**
103
*/
104
function su_accordion_shortcode( $atts = null, $content = null ) {
105
106
+ return '<div class="su-accordion">' . su_do_shortcode( $content, 'a' ) . '</div>';
107
}
108
109
/**
152
return '<span class="su-highlight" style="background:' . $bg . ';color:' . $color . '">&nbsp;' . $content . '&nbsp;</span>';
153
}
154
155
+ /**
156
+ * Shortcode: label
157
+ *
158
+ * @param array $atts Shortcode attributes
159
+ * @param string $content
160
+ * @return string Output html
161
+ */
162
+ function su_label_shortcode( $atts, $content = null ) {
163
+ extract( shortcode_atts( array(
164
+ 'style' => 'default'
165
+ ), $atts ) );
166
+
167
+ return '<span class="su-label su-label-style-' . $style . '">' . $content . '</span>';
168
+ }
169
+
170
/**
171
* Shortcode: dropcap
172
*
199
'style' => 0
200
), $atts ) );
201
202
+ return ( $last ) ? '<div class="su-column su-column-' . $size . ' su-column-last su-column-style-' . $style . '">' . su_do_shortcode( $content, 'c' ) . '</div><div class="su-spacer"></div>' : '<div class="su-column su-column-' . $size . ' su-column-style-' . $style . '">' . su_do_shortcode( $content, 'c' ) . '</div>';
203
}
204
205
/**
260
'link' => '#',
261
'color' => '#aaa',
262
'dark' => false,
263
+ 'radius' => 'auto',
264
'square' => false,
265
'style' => 1,
266
'size' => 3,
269
'target' => false
270
), $atts ) );
271
272
+ // Old parameter compatibility, square
273
+ $radius = ( $square ) ? 0 : $radius;
274
+
275
$styles = array(
276
+ 'border_radius' => ( $radius == 'auto' ) ? round( $size + 2 ) : intval( $radius ),
277
'dark_color' => su_hex_shift( $color, 'darker', 20 ),
278
'light_color' => su_hex_shift( $color, 'lighter', 70 ),
279
'size' => round( ( $size + 9 ) * 1.3 ),
382
'text_shadow' => su_hex_shift( $color, 'darker', 70 ),
383
);
384
385
+ return '<div class="su-box" style="border:1px solid ' . $styles['dark_color'] . '"><div class="su-box-title" style="background-color:' . $color . ';border-top:1px solid ' . $styles['light_color'] . ';text-shadow:1px 1px 0 ' . $styles['text_shadow'] . '">' . $title . '</div><div class="su-box-content">' . su_do_shortcode( $content, 'b' ) . '</div></div>';
386
}
387
388
/**
404
'text_color' => su_hex_shift( $color, 'darker', 70 )
405
);
406
407
+ return '<div class="su-note" style="background-color:' . $styles['light_color'] . ';border:1px solid ' . $styles['dark_color'] . '"><div class="su-note-shell" style="border:1px solid ' . $styles['extra_light_color'] . ';color:' . $styles['text_color'] . '">' . su_do_shortcode( $content, 'n' ) . '</div></div>';
408
}
409
410
/**
lib/timthumb.php CHANGED
@@ -1,1188 +1,1223 @@
1
- <?php
2
- /**
3
- * TimThumb by Ben Gillbanks and Mark Maunder
4
- * Based on work done by Tim McDaniels and Darren Hoyt
5
- * http://code.google.com/p/timthumb/
6
- *
7
- * GNU General Public License, version 2
8
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9
- *
10
- * Examples and documentation available on the project homepage
11
- * http://www.binarymoon.co.uk/projects/timthumb/
12
- */
13
-
14
- /*
15
- -----TimThumb CONFIGURATION-----
16
- You can either edit the configuration variables manually here, or you can
17
- create a file called timthumb-config.php and define variables you want
18
- to customize in there. It will automatically be loaded by timthumb.
19
- This will save you having to re-edit these variables everytime you download
20
- a new version of timthumb.
21
-
22
- */
23
- define ('VERSION', '2.8'); // Version of this script
24
- //Load a config file if it exists. Otherwise, use the values below.
25
- if( file_exists('timthumb-config.php')) require_once('timthumb-config.php');
26
- if(! defined( 'DEBUG_ON' ) ) define ('DEBUG_ON', false); // Enable debug logging to web server error log (STDERR)
27
- if(! defined('DEBUG_LEVEL') ) define ('DEBUG_LEVEL', 1); // Debug level 1 is less noisy and 3 is the most noisy
28
- if(! defined('MEMORY_LIMIT') ) define ('MEMORY_LIMIT', '30M'); // Set PHP memory limit
29
- if(! defined('BLOCK_EXTERNAL_LEECHERS') ) define ('BLOCK_EXTERNAL_LEECHERS', false); // If the image or webshot is being loaded on an external site, display a red "No Hotlinking" gif.
30
-
31
- //Image fetching and caching
32
- if(! defined('ALLOW_EXTERNAL') ) define ('ALLOW_EXTERNAL', TRUE); // Allow image fetching from external websites. Will check against ALLOWED_SITES if ALLOW_ALL_EXTERNAL_SITES is false
33
- if(! defined('ALLOW_ALL_EXTERNAL_SITES') ) define ('ALLOW_ALL_EXTERNAL_SITES', false); // Less secure.
34
- if(! defined('FILE_CACHE_ENABLED') ) define ('FILE_CACHE_ENABLED', TRUE); // Should we store resized/modified images on disk to speed things up?
35
- if(! defined('FILE_CACHE_TIME_BETWEEN_CLEANS')) define ('FILE_CACHE_TIME_BETWEEN_CLEANS', 86400); // How often the cache is cleaned
36
- if(! defined('FILE_CACHE_MAX_FILE_AGE') ) define ('FILE_CACHE_MAX_FILE_AGE', 86400); // How old does a file have to be to be deleted from the cache
37
- if(! defined('FILE_CACHE_SUFFIX') ) define ('FILE_CACHE_SUFFIX', '.timthumb.txt'); // What to put at the end of all files in the cache directory so we can identify them
38
- if(! defined('FILE_CACHE_DIRECTORY') ) define ('FILE_CACHE_DIRECTORY', './cache'); // Directory where images are cached. Left blank it will use the system temporary directory (which is better for security)
39
- if(! defined('MAX_FILE_SIZE') ) define ('MAX_FILE_SIZE', 10485760); // 10 Megs is 10485760. This is the max internal or external file size that we'll process.
40
- if(! defined('CURL_TIMEOUT') ) define ('CURL_TIMEOUT', 20); // Timeout duration for Curl. This only applies if you have Curl installed and aren't using PHP's default URL fetching mechanism.
41
- if(! defined('WAIT_BETWEEN_FETCH_ERRORS') ) define ('WAIT_BETWEEN_FETCH_ERRORS', 3600); //Time to wait between errors fetching remote file
42
- //Browser caching
43
- if(! defined('BROWSER_CACHE_MAX_AGE') ) define ('BROWSER_CACHE_MAX_AGE', 864000); // Time to cache in the browser
44
- if(! defined('BROWSER_CACHE_DISABLE') ) define ('BROWSER_CACHE_DISABLE', false); // Use for testing if you want to disable all browser caching
45
-
46
- //Image size and defaults
47
- if(! defined('MAX_WIDTH') ) define ('MAX_WIDTH', 1500); // Maximum image width
48
- if(! defined('MAX_HEIGHT') ) define ('MAX_HEIGHT', 1500); // Maximum image height
49
- if(! defined('NOT_FOUND_IMAGE') ) define ('NOT_FOUND_IMAGE', ''); //Image to serve if any 404 occurs
50
- if(! defined('ERROR_IMAGE') ) define ('ERROR_IMAGE', ''); //Image to serve if an error occurs instead of showing error message
51
-
52
- //Image compression is enabled if either of these point to valid paths
53
-
54
- //These are now disabled by default because the file sizes of PNGs (and GIFs) are much smaller than we used to generate.
55
- //They only work for PNGs. GIFs and JPEGs are not affected.
56
- if(! defined('OPTIPNG_ENABLED') ) define ('OPTIPNG_ENABLED', false);
57
- if(! defined('OPTIPNG_PATH') ) define ('OPTIPNG_PATH', '/usr/bin/optipng'); //This will run first because it gives better compression than pngcrush.
58
- if(! defined('PNGCRUSH_ENABLED') ) define ('PNGCRUSH_ENABLED', false);
59
- if(! defined('PNGCRUSH_PATH') ) define ('PNGCRUSH_PATH', '/usr/bin/pngcrush'); //This will only run if OPTIPNG_PATH is not set or is not valid
60
-
61
- /*
62
- -------====Website Screenshots configuration - BETA====-------
63
-
64
- If you just want image thumbnails and don't want website screenshots, you can safely leave this as is.
65
-
66
- If you would like to get website screenshots set up, you will need root access to your own server.
67
-
68
- Enable ALLOW_ALL_EXTERNAL_SITES so you can fetch any external web page. This is more secure now that we're using a non-web folder for cache.
69
- Enable BLOCK_EXTERNAL_LEECHERS so that your site doesn't generate thumbnails for the whole Internet.
70
-
71
- Instructions to get website screenshots enabled on Ubuntu Linux:
72
-
73
- 1. Install Xvfb with the following command: sudo apt-get install subversion libqt4-webkit libqt4-dev g++ xvfb
74
- 2. Go to a directory where you can download some code
75
- 3. Check-out the latest version of CutyCapt with the following command: svn co https://cutycapt.svn.sourceforge.net/svnroot/cutycapt
76
- 4. Compile CutyCapt by doing: cd cutycapt/CutyCapt
77
- 5. qmake
78
- 6. make
79
- 7. cp CutyCapt /usr/local/bin/
80
- 8. Test it by running: xvfb-run --server-args="-screen 0, 1024x768x24" CutyCapt --url="http://markmaunder.com/" --out=test.png
81
- 9. If you get a file called test.png with something in it, it probably worked. Now test the script by accessing it as follows:
82
- 10. http://yoursite.com/path/to/timthumb.php?src=http://markmaunder.com/&webshot=1
83
-
84
- Notes on performance:
85
- The first time a webshot loads, it will take a few seconds.
86
- From then on it uses the regular timthumb caching mechanism with the configurable options above
87
- and loading will be very fast.
88
-
89
- --ADVANCED USERS ONLY--
90
- If you'd like a slight speedup (about 25%) and you know Linux, you can run the following command which will keep Xvfb running in the background.
91
- nohup Xvfb :100 -ac -nolisten tcp -screen 0, 1024x768x24 > /dev/null 2>&1 &
92
- Then set WEBSHOT_XVFB_RUNNING = true below. This will save your server having to fire off a new Xvfb server and shut it down every time a new shot is generated.
93
- You will need to take responsibility for keeping Xvfb running in case it crashes. (It seems pretty stable)
94
- You will also need to take responsibility for server security if you're running Xvfb as root.
95
-
96
-
97
- */
98
- if(! defined('WEBSHOT_ENABLED') ) define ('WEBSHOT_ENABLED', false); //Beta feature. Adding webshot=1 to your query string will cause the script to return a browser screenshot rather than try to fetch an image.
99
- if(! defined('WEBSHOT_CUTYCAPT') ) define ('WEBSHOT_CUTYCAPT', '/usr/local/bin/CutyCapt'); //The path to CutyCapt.
100
- if(! defined('WEBSHOT_XVFB') ) define ('WEBSHOT_XVFB', '/usr/bin/xvfb-run'); //The path to the Xvfb server
101
- if(! defined('WEBSHOT_SCREEN_X') ) define ('WEBSHOT_SCREEN_X', '1024'); //1024 works ok
102
- if(! defined('WEBSHOT_SCREEN_Y') ) define ('WEBSHOT_SCREEN_Y', '768'); //768 works ok
103
- if(! defined('WEBSHOT_COLOR_DEPTH') ) define ('WEBSHOT_COLOR_DEPTH', '24'); //I haven't tested anything besides 24
104
- if(! defined('WEBSHOT_IMAGE_FORMAT') ) define ('WEBSHOT_IMAGE_FORMAT', 'png'); //png is about 2.5 times the size of jpg but is a LOT better quality
105
- if(! defined('WEBSHOT_TIMEOUT') ) define ('WEBSHOT_TIMEOUT', '20'); //Seconds to wait for a webshot
106
- if(! defined('WEBSHOT_USER_AGENT') ) define ('WEBSHOT_USER_AGENT', "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"); //I hate to do this, but a non-browser robot user agent might not show what humans see. So we pretend to be Firefox
107
- if(! defined('WEBSHOT_JAVASCRIPT_ON') ) define ('WEBSHOT_JAVASCRIPT_ON', true); //Setting to false might give you a slight speedup and block ads. But it could cause other issues.
108
- if(! defined('WEBSHOT_JAVA_ON') ) define ('WEBSHOT_JAVA_ON', false); //Have only tested this as fase
109
- if(! defined('WEBSHOT_PLUGINS_ON') ) define ('WEBSHOT_PLUGINS_ON', true); //Enable flash and other plugins
110
- if(! defined('WEBSHOT_PROXY') ) define ('WEBSHOT_PROXY', ''); //In case you're behind a proxy server.
111
- if(! defined('WEBSHOT_XVFB_RUNNING') ) define ('WEBSHOT_XVFB_RUNNING', false); //ADVANCED: Enable this if you've got Xvfb running in the background.
112
-
113
-
114
- // If ALLOW_EXTERNAL is true and ALLOW_ALL_EXTERNAL_SITES is false, then external images will only be fetched from these domains and their subdomains.
115
- if(! isset($ALLOWED_SITES)){
116
- $ALLOWED_SITES = array (
117
- 'flickr.com',
118
- 'picasa.com',
119
- 'img.youtube.com',
120
- 'upload.wikimedia.org',
121
- 'photobucket.com',
122
- 'imgur.com',
123
- 'imageshack.us',
124
- 'tinypic.com'
125
- );
126
- }
127
- // -------------------------------------------------------------
128
- // -------------- STOP EDITING CONFIGURATION HERE --------------
129
- // -------------------------------------------------------------
130
-
131
- timthumb::start();
132
-
133
- class timthumb {
134
- protected $src = "";
135
- protected $is404 = false;
136
- protected $docRoot = "";
137
- protected $lastURLError = false;
138
- protected $localImage = "";
139
- protected $localImageMTime = 0;
140
- protected $url = false;
141
- protected $myHost = "";
142
- protected $isURL = false;
143
- protected $cachefile = '';
144
- protected $errors = array();
145
- protected $toDeletes = array();
146
- protected $cacheDirectory = '';
147
- protected $startTime = 0;
148
- protected $lastBenchTime = 0;
149
- protected $cropTop = false;
150
- protected $salt = "";
151
- protected $fileCacheVersion = 1; //Generally if timthumb.php is modifed (upgraded) then the salt changes and all cache files are recreated. This is a backup mechanism to force regen.
152
- protected $filePrependSecurityBlock = "<?php die('Execution denied!'); //"; //Designed to have three letter mime type, space, question mark and greater than symbol appended. 6 bytes total.
153
- protected static $curlDataWritten = 0;
154
- protected static $curlFH = false;
155
- public static function start(){
156
- $tim = new timthumb();
157
- $tim->handleErrors();
158
- $tim->securityChecks();
159
- if($tim->tryBrowserCache()){
160
- exit(0);
161
- }
162
- $tim->handleErrors();
163
- if(FILE_CACHE_ENABLED && $tim->tryServerCache()){
164
- exit(0);
165
- }
166
- $tim->handleErrors();
167
- $tim->run();
168
- $tim->handleErrors();
169
- exit(0);
170
- }
171
- public function __construct(){
172
- global $ALLOWED_SITES;
173
- $this->startTime = microtime(true);
174
- $this->debug(1, "Starting new request from " . $this->getIP() . " to " . $_SERVER['REQUEST_URI']);
175
- $this->calcDocRoot();
176
- //On windows systems I'm assuming fileinode returns an empty string or a number that doesn't change. Check this.
177
- $this->salt = @filemtime(__FILE__) . '-' . @fileinode(__FILE__);
178
- $this->debug(3, "Salt is: " . $this->salt);
179
- if(FILE_CACHE_DIRECTORY){
180
- if(! is_dir(FILE_CACHE_DIRECTORY)){
181
- @mkdir(FILE_CACHE_DIRECTORY);
182
- if(! is_dir(FILE_CACHE_DIRECTORY)){
183
- $this->error("Could not create the file cache directory.");
184
- return false;
185
- }
186
- }
187
- $this->cacheDirectory = FILE_CACHE_DIRECTORY;
188
- touch($this->cacheDirectory . '/index.html');
189
- } else {
190
- $this->cacheDirectory = sys_get_temp_dir();
191
- }
192
- //Clean the cache before we do anything because we don't want the first visitor after FILE_CACHE_TIME_BETWEEN_CLEANS expires to get a stale image.
193
- $this->cleanCache();
194
-
195
- $this->myHost = preg_replace('/^www\./i', '', $_SERVER['HTTP_HOST']);
196
- $this->src = $this->param('src');
197
- $this->url = parse_url($this->src);
198
- if(strlen($this->src) <= 3){
199
- $this->error("No image specified");
200
- return false;
201
- }
202
- if(BLOCK_EXTERNAL_LEECHERS && array_key_exists('HTTP_REFERER', $_SERVER) && (! preg_match('/^https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $_SERVER['HTTP_REFERER']))){
203
- // base64 encoded red image that says 'no hotlinkers'
204
- // nothing to worry about! :)
205
- $imgData = base64_decode("R0lGODlhUAAMAIAAAP8AAP///yH5BAAHAP8ALAAAAABQAAwAAAJpjI+py+0Po5y0OgAMjjv01YUZ\nOGplhWXfNa6JCLnWkXplrcBmW+spbwvaVr/cDyg7IoFC2KbYVC2NQ5MQ4ZNao9Ynzjl9ScNYpneb\nDULB3RP6JuPuaGfuuV4fumf8PuvqFyhYtjdoeFgAADs=");
206
- header('Content-Type: image/gif');
207
- header('Content-Length: ' . sizeof($imgData));
208
- header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
209
- header("Pragma: no-cache");
210
- header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
211
- echo $imgData;
212
- return false;
213
- exit(0);
214
- }
215
- if(preg_match('/https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $this->src)){
216
- $this->src = preg_replace('/https?:\/\/(?:www\.)?' . $this->myHost . '/i', '', $this->src);
217
- }
218
- if(preg_match('/^https?:\/\/[^\/]+/i', $this->src)){
219
- $this->debug(2, "Is a request for an external URL: " . $this->src);
220
- $this->isURL = true;
221
- } else {
222
- $this->debug(2, "Is a request for an internal file: " . $this->src);
223
- }
224
- if($this->isURL && (! ALLOW_EXTERNAL)){
225
- $this->error("You are not allowed to fetch images from an external website.");
226
- return false;
227
- }
228
- if($this->isURL){
229
- if(ALLOW_ALL_EXTERNAL_SITES){
230
- $this->debug(2, "Fetching from all external sites is enabled.");
231
- } else {
232
- $this->debug(2, "Fetching only from selected external sites is enabled.");
233
- $allowed = false;
234
- foreach($ALLOWED_SITES as $site){
235
- if (preg_match ('/(?:^|\.)' . $site . '#x2F;i', $this->url['host'])) {
236
- $this->debug(3, "URL hostname {$this->url['host']} matches $site so allowing.");
237
- $allowed = true;
238
- }
239
- }
240
- if(! $allowed){
241
- return $this->error("You may not fetch images from that site. To enable this site in timthumb, you can either add it to \$ALLOWED_SITES and set ALLOW_EXTERNAL=true. Or you can set ALLOW_ALL_EXTERNAL_SITES=true, depending on your security needs.");
242
- }
243
- }
244
- }
245
-
246
- $cachePrefix = ($this->isURL ? 'timthumb_ext_' : 'timthumb_int_');
247
- if($this->isURL){
248
- $this->cachefile = $this->cacheDirectory . '/' . $cachePrefix . md5($this->salt . $_SERVER ['QUERY_STRING'] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
249
- } else {
250
- $this->localImage = $this->getLocalImagePath($this->src);
251
- if(! $this->localImage){
252
- $this->debug(1, "Could not find the local image: {$this->localImage}");
253
- $this->error("Could not find the internal image you specified.");
254
- $this->set404();
255
- return false;
256
- }
257
- $this->debug(1, "Local image path is {$this->localImage}");
258
- $this->localImageMTime = @filemtime($this->localImage);
259
- //We include the mtime of the local file in case in changes on disk.
260
- $this->cachefile = $this->cacheDirectory . '/' . $cachePrefix . md5($this->salt . $this->localImageMTime . $_SERVER ['QUERY_STRING'] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
261
- }
262
- $this->debug(2, "Cache file is: " . $this->cachefile);
263
-
264
- return true;
265
- }
266
- public function __destruct(){
267
- foreach($this->toDeletes as $del){
268
- $this->debug(2, "Deleting temp file $del");
269
- @unlink($del);
270
- }
271
- }
272
- public function run(){
273
- if($this->isURL){
274
- if(! ALLOW_EXTERNAL){
275
- $this->debug(1, "Got a request for an external image but ALLOW_EXTERNAL is disabled so returning error msg.");
276
- $this->error("You are not allowed to fetch images from an external website.");
277
- return false;
278
- }
279
- $this->debug(3, "Got request for external image. Starting serveExternalImage.");
280
- if($this->param('webshot')){
281
- if(WEBSHOT_ENABLED){
282
- $this->debug(3, "webshot param is set, so we're going to take a webshot.");
283
- $this->serveWebshot();
284
- } else {
285
- $this->error("You added the webshot parameter but webshots are disabled on this server. You need to set WEBSHOT_ENABLED == true to enable webshots.");
286
- }
287
- } else {
288
- $this->debug(3, "webshot is NOT set so we're going to try to fetch a regular image.");
289
- $this->serveExternalImage();
290
-
291
- }
292
- } else {
293
- $this->debug(3, "Got request for internal image. Starting serveInternalImage()");
294
- $this->serveInternalImage();
295
- }
296
- return true;
297
- }
298
- protected function handleErrors(){
299
- if($this->haveErrors()){
300
- if(NOT_FOUND_IMAGE && $this->is404()){
301
- if($this->serveImg(NOT_FOUND_IMAGE)){
302
- exit(0);
303
- } else {
304
- $this->error("Additionally, the 404 image that is configured could not be found or there was an error serving it.");
305
- }
306
- }
307
- if(ERROR_IMAGE){
308
- if($this->serveImg(ERROR_IMAGE)){
309
- exit(0);
310
- } else {
311
- $this->error("Additionally, the error image that is configured could not be found or there was an error serving it.");
312
- }
313
- }
314
-
315
- $this->serveErrors();
316
- exit(0);
317
- }
318
- return false;
319
- }
320
- protected function tryBrowserCache(){
321
- if(BROWSER_CACHE_DISABLE){ $this->debug(3, "Browser caching is disabled"); return false; }
322
- if(!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ){
323
- $this->debug(3, "Got a conditional get");
324
- $mtime = false;
325
- //We've already checked if the real file exists in the constructor
326
- if(! is_file($this->cachefile)){
327
- //If we don't have something cached, regenerate the cached image.
328
- return false;
329
- }
330
- if($this->localImageMTime){
331
- $mtime = $this->localImageMTime;
332
- $this->debug(3, "Local real file's modification time is $mtime");
333
- } else if(is_file($this->cachefile)){ //If it's not a local request then use the mtime of the cached file to determine the 304
334
- $mtime = @filemtime($this->cachefile);
335
- $this->debug(3, "Cached file's modification time is $mtime");
336
- }
337
- if(! $mtime){ return false; }
338
-
339
- $iftime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
340
- $this->debug(3, "The conditional get's if-modified-since unixtime is $iftime");
341
- if($iftime < 1){
342
- $this->debug(3, "Got an invalid conditional get modified since time. Returning false.");
343
- return false;
344
- }
345
- if($iftime < $mtime){ //Real file or cache file has been modified since last request, so force refetch.
346
- $this->debug(3, "File has been modified since last fetch.");
347
- return false;
348
- } else { //Otherwise serve a 304
349
- $this->debug(3, "File has not been modified since last get, so serving a 304.");
350
- header ('HTTP/1.1 304 Not Modified');
351
- $this->debug(1, "Returning 304 not modified");
352
- return true;
353
- }
354
- }
355
- return false;
356
- }
357
- protected function tryServerCache(){
358
- $this->debug(3, "Trying server cache");
359
- if(file_exists($this->cachefile)){
360
- $this->debug(3, "Cachefile {$this->cachefile} exists");
361
- if($this->isURL){
362
- $this->debug(3, "This is an external request, so checking if the cachefile is empty which means the request failed previously.");
363
- if(filesize($this->cachefile) < 1){
364
- $this->debug(3, "Found an empty cachefile indicating a failed earlier request. Checking how old it is.");
365
- //Fetching error occured previously
366
- if(time() - @filemtime($this->cachefile) > WAIT_BETWEEN_FETCH_ERRORS){
367
- $this->debug(3, "File is older than " . WAIT_BETWEEN_FETCH_ERRORS . " seconds. Deleting and returning false so app can try and load file.");
368
- @unlink($this->cachefile);
369
- return false; //to indicate we didn't serve from cache and app should try and load
370
- } else {
371
- $this->debug(3, "Empty cachefile is still fresh so returning message saying we had an error fetching this image from remote host.");
372
- $this->set404();
373
- $this->error("An error occured fetching image.");
374
- return false;
375
- }
376
- }
377
- } else {
378
- $this->debug(3, "Trying to serve cachefile {$this->cachefile}");
379
- }
380
- if($this->serveCacheFile()){
381
- $this->debug(3, "Succesfully served cachefile {$this->cachefile}");
382
- return true;
383
- } else {
384
- $this->debug(3, "Failed to serve cachefile {$this->cachefile} - Deleting it from cache.");
385
- //Image serving failed. We can't retry at this point, but lets remove it from cache so the next request recreates it
386
- @unlink($this->cachefile);
387
- return true;
388
- }
389
- }
390
- }
391
- protected function error($err){
392
- $this->debug(3, "Adding error message: $err");
393
- $this->errors[] = $err;
394
- return false;
395
-
396
- }
397
- protected function haveErrors(){
398
- if(sizeof($this->errors) > 0){
399
- return true;
400
- }
401
- return false;
402
- }
403
- protected function serveErrors(){
404
- $html = '<ul>';
405
- foreach($this->errors as $err){
406
- $html .= '<li>' . htmlentities($err) . '</li>';
407
- }
408
- $html .= '</ul>';
409
- header ('HTTP/1.1 400 Bad Request');
410
- echo '<h1>A TimThumb error has occured</h1>The following error(s) occured:<br />' . $html . '<br />';
411
- echo '<br />Query String : ' . htmlentities ($_SERVER['QUERY_STRING']);
412
- echo '<br />TimThumb version : ' . VERSION . '</pre>';
413
- }
414
- protected function serveInternalImage(){
415
- $this->debug(3, "Local image path is $this->localImage");
416
- if(! $this->localImage){
417
- $this->sanityFail("localImage not set after verifying it earlier in the code.");
418
- return false;
419
- }
420
- $fileSize = filesize($this->localImage);
421
- if($fileSize > MAX_FILE_SIZE){
422
- $this->error("The file you specified is greater than the maximum allowed file size.");
423
- return false;
424
- }
425
- if($fileSize <= 0){
426
- $this->error("The file you specified is <= 0 bytes.");
427
- return false;
428
- }
429
- $this->debug(3, "Calling processImageAndWriteToCache() for local image.");
430
- if($this->processImageAndWriteToCache($this->localImage)){
431
- $this->serveCacheFile();
432
- return true;
433
- } else {
434
- return false;
435
- }
436
- }
437
- protected function cleanCache(){
438
- $this->debug(3, "cleanCache() called");
439
- $lastCleanFile = $this->cacheDirectory . '/timthumb_cacheLastCleanTime.touch';
440
-
441
- //If this is a new timthumb installation we need to create the file
442
- if(! is_file($lastCleanFile)){
443
- $this->debug(1, "File tracking last clean doesn't exist. Creating $lastCleanFile");
444
- touch($lastCleanFile);
445
- return;
446
- }
447
- if(@filemtime($lastCleanFile) < (time() - FILE_CACHE_TIME_BETWEEN_CLEANS) ){ //Cache was last cleaned more than 1 day ago
448
- $this->debug(1, "Cache was last cleaned more than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago. Cleaning now.");
449
- // Very slight race condition here, but worst case we'll have 2 or 3 servers cleaning the cache simultaneously once a day.
450
- touch($lastCleanFile);
451
- $files = glob($this->cacheDirectory . '/*' . FILE_CACHE_SUFFIX);
452
- $timeAgo = time() - FILE_CACHE_MAX_FILE_AGE;
453
- foreach($files as $file){
454
- if(@filemtime($file) < $timeAgo){
455
- $this->debug(3, "Deleting cache file $file older than max age: " . FILE_CACHE_MAX_FILE_AGE . " seconds");
456
- @unlink($file);
457
- }
458
- }
459
- return true;
460
- } else {
461
- $this->debug(3, "Cache was cleaned less than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago so no cleaning needed.");
462
- }
463
- return false;
464
- }
465
- protected function processImageAndWriteToCache($localImage){
466
- $sData = getimagesize($localImage);
467
- $origType = $sData[2];
468
- $mimeType = $sData['mime'];
469
-
470
- $this->debug(3, "Mime type of image is $mimeType");
471
- if(! preg_match('/^image\/(?:gif|jpg|jpeg|png)#x2F;i', $mimeType)){
472
- return $this->error("The image being resized is not a valid gif, jpg or png.");
473
- }
474
-
475
- if (!function_exists ('imagecreatetruecolor')) {
476
- return $this->error('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library');
477
- }
478
-
479
- if (function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
480
- $imageFilters = array (
481
- 1 => array (IMG_FILTER_NEGATE, 0),
482
- 2 => array (IMG_FILTER_GRAYSCALE, 0),
483
- 3 => array (IMG_FILTER_BRIGHTNESS, 1),
484
- 4 => array (IMG_FILTER_CONTRAST, 1),
485
- 5 => array (IMG_FILTER_COLORIZE, 4),
486
- 6 => array (IMG_FILTER_EDGEDETECT, 0),
487
- 7 => array (IMG_FILTER_EMBOSS, 0),
488
- 8 => array (IMG_FILTER_GAUSSIAN_BLUR, 0),
489
- 9 => array (IMG_FILTER_SELECTIVE_BLUR, 0),
490
- 10 => array (IMG_FILTER_MEAN_REMOVAL, 0),
491
- 11 => array (IMG_FILTER_SMOOTH, 0),
492
- );
493
- }
494
-
495
- // get standard input properties
496
- $new_width = (int) abs ($this->param('w', 0));
497
- $new_height = (int) abs ($this->param('h', 0));
498
- $zoom_crop = (int) $this->param('zc', 1);
499
- $quality = (int) abs ($this->param('q', 90));
500
- $align = $this->cropTop ? 't' : $this->param('a', 'c');
501
- $filters = $this->param('f', '');
502
- $sharpen = (bool) $this->param('s', 0);
503
- $canvas_color = $this->param('cc', 'ffffff');
504
-
505
- // set default width and height if neither are set already
506
- if ($new_width == 0 && $new_height == 0) {
507
- $new_width = 100;
508
- $new_height = 100;
509
- }
510
-
511
- // ensure size limits can not be abused
512
- $new_width = min ($new_width, MAX_WIDTH);
513
- $new_height = min ($new_height, MAX_HEIGHT);
514
-
515
- // set memory limit to be able to have enough space to resize larger images
516
- $this->setMemoryLimit();
517
-
518
- // open the existing image
519
- $image = $this->openImage ($mimeType, $localImage);
520
- if ($image === false) {
521
- return $this->error('Unable to open image.');
522
- }
523
-
524
- // Get original width and height
525
- $width = imagesx ($image);
526
- $height = imagesy ($image);
527
- $origin_x = 0;
528
- $origin_y = 0;
529
-
530
- // generate new w/h if not provided
531
- if ($new_width && !$new_height) {
532
- $new_height = floor ($height * ($new_width / $width));
533
- } else if ($new_height && !$new_width) {
534
- $new_width = floor ($width * ($new_height / $height));
535
- }
536
-
537
- // scale down and add borders
538
- if ($zoom_crop == 3) {
539
-
540
- $final_height = $height * ($new_width / $width);
541
-
542
- if ($final_height > $new_height) {
543
- $new_width = $width * ($new_height / $height);
544
- } else {
545
- $new_height = $final_height;
546
- }
547
-
548
- }
549
-
550
- // create a new true color image
551
- $canvas = imagecreatetruecolor ($new_width, $new_height);
552
- imagealphablending ($canvas, false);
553
-
554
- if (strlen ($canvas_color) < 6) {
555
- $canvas_color = 'ffffff';
556
- }
557
-
558
- $canvas_color_R = hexdec (substr ($canvas_color, 0, 2));
559
- $canvas_color_G = hexdec (substr ($canvas_color, 2, 2));
560
- $canvas_color_B = hexdec (substr ($canvas_color, 2, 2));
561
-
562
- // Create a new transparent color for image
563
- $color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 127);
564
-
565
- // Completely fill the background of the new image with allocated color.
566
- imagefill ($canvas, 0, 0, $color);
567
-
568
- // scale down and add borders
569
- if ($zoom_crop == 2) {
570
-
571
- $final_height = $height * ($new_width / $width);
572
-
573
- if ($final_height > $new_height) {
574
-
575
- $origin_x = $new_width / 2;
576
- $new_width = $width * ($new_height / $height);
577
- $origin_x = round ($origin_x - ($new_width / 2));
578
-
579
- } else {
580
-
581
- $origin_y = $new_height / 2;
582
- $new_height = $final_height;
583
- $origin_y = round ($origin_y - ($new_height / 2));
584
-
585
- }
586
-
587
- }
588
-
589
- // Restore transparency blending
590
- imagesavealpha ($canvas, true);
591
-
592
- if ($zoom_crop > 0) {
593
-
594
- $src_x = $src_y = 0;
595
- $src_w = $width;
596
- $src_h = $height;
597
-
598
- $cmp_x = $width / $new_width;
599
- $cmp_y = $height / $new_height;
600
-
601
- // calculate x or y coordinate and width or height of source
602
- if ($cmp_x > $cmp_y) {
603
-
604
- $src_w = round ($width / $cmp_x * $cmp_y);
605
- $src_x = round (($width - ($width / $cmp_x * $cmp_y)) / 2);
606
-
607
- } else if ($cmp_y > $cmp_x) {
608
-
609
- $src_h = round ($height / $cmp_y * $cmp_x);
610
- $src_y = round (($height - ($height / $cmp_y * $cmp_x)) / 2);
611
-
612
- }
613
-
614
- // positional cropping!
615
- if ($align) {
616
- if (strpos ($align, 't') !== false) {
617
- $src_y = 0;
618
- }
619
- if (strpos ($align, 'b') !== false) {
620
- $src_y = $height - $src_h;
621
- }
622
- if (strpos ($align, 'l') !== false) {
623
- $src_x = 0;
624
- }
625
- if (strpos ($align, 'r') !== false) {
626
- $src_x = $width - $src_w;
627
- }
628
- }
629
-
630
- imagecopyresampled ($canvas, $image, $origin_x, $origin_y, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h);
631
-
632
- } else {
633
-
634
- // copy and resize part of an image with resampling
635
- imagecopyresampled ($canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
636
-
637
- }
638
-
639
- if ($filters != '' && function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
640
- // apply filters to image
641
- $filterList = explode ('|', $filters);
642
- foreach ($filterList as $fl) {
643
-
644
- $filterSettings = explode (',', $fl);
645
- if (isset ($imageFilters[$filterSettings[0]])) {
646
-
647
- for ($i = 0; $i < 4; $i ++) {
648
- if (!isset ($filterSettings[$i])) {
649
- $filterSettings[$i] = null;
650
- } else {
651
- $filterSettings[$i] = (int) $filterSettings[$i];
652
- }
653
- }
654
-
655
- switch ($imageFilters[$filterSettings[0]][1]) {
656
-
657
- case 1:
658
-
659
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]);
660
- break;
661
-
662
- case 2:
663
-
664
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]);
665
- break;
666
-
667
- case 3:
668
-
669
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]);
670
- break;
671
-
672
- case 4:
673
-
674
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3], $filterSettings[4]);
675
- break;
676
-
677
- default:
678
-
679
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0]);
680
- break;
681
-
682
- }
683
- }
684
- }
685
- }
686
-
687
- // sharpen image
688
- if ($sharpen && function_exists ('imageconvolution')) {
689
-
690
- $sharpenMatrix = array (
691
- array (-1,-1,-1),
692
- array (-1,16,-1),
693
- array (-1,-1,-1),
694
- );
695
-
696
- $divisor = 8;
697
- $offset = 0;
698
-
699
- imageconvolution ($canvas, $sharpenMatrix, $divisor, $offset);
700
-
701
- }
702
- //Straight from Wordpress core code. Reduces filesize by up to 70% for PNG's
703
- if ( (IMAGETYPE_PNG == $origType || IMAGETYPE_GIF == $origType) && function_exists('imageistruecolor') && !imageistruecolor( $image ) && imagecolortransparent( $image ) > 0 ){
704
- imagetruecolortopalette( $canvas, false, imagecolorstotal( $image ) );
705
- }
706
-
707
- $imgType = "";
708
- $tempfile = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
709
- if(preg_match('/^image\/(?:jpg|jpeg)#x2F;i', $mimeType)){
710
- $imgType = 'jpg';
711
- imagejpeg($canvas, $tempfile, $quality);
712
- } else if(preg_match('/^image\/png#x2F;i', $mimeType)){
713
- $imgType = 'png';
714
- imagepng($canvas, $tempfile, floor($quality * 0.09));
715
- } else if(preg_match('/^image\/gif#x2F;i', $mimeType)){
716
- $imgType = 'gif';
717
- imagegif($canvas, $tempfile);
718
- } else {
719
- return $this->sanityFail("Could not match mime type after verifying it previously.");
720
- }
721
-
722
- if($imgType == 'png' && OPTIPNG_ENABLED && OPTIPNG_PATH && @is_file(OPTIPNG_PATH)){
723
- $exec = OPTIPNG_PATH;
724
- $this->debug(3, "optipng'ing $tempfile");
725
- $presize = filesize($tempfile);
726
- $out = `$exec -o1 $tempfile`; //you can use up to -o7 but it really slows things down
727
- clearstatcache();
728
- $aftersize = filesize($tempfile);
729
- $sizeDrop = $presize - $aftersize;
730
- if($sizeDrop > 0){
731
- $this->debug(1, "optipng reduced size by $sizeDrop");
732
- } else if($sizeDrop < 0){
733
- $this->debug(1, "optipng increased size! Difference was: $sizeDrop");
734
- } else {
735
- $this->debug(1, "optipng did not change image size.");
736
- }
737
- } else if($imgType == 'png' && PNGCRUSH_ENABLED && PNGCRUSH_PATH && @is_file(PNGCRUSH_PATH)){
738
- $exec = PNGCRUSH_PATH;
739
- $tempfile2 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
740
- $this->debug(3, "pngcrush'ing $tempfile to $tempfile2");
741
- $out = `$exec $tempfile $tempfile2`;
742
- $todel = "";
743
- if(is_file($tempfile2)){
744
- $sizeDrop = filesize($tempfile) - filesize($tempfile2);
745
- if($sizeDrop > 0){
746
- $this->debug(1, "pngcrush was succesful and gave a $sizeDrop byte size reduction");
747
- $todel = $tempfile;
748
- $tempfile = $tempfile2;
749
- } else {
750
- $this->debug(1, "pngcrush did not reduce file size. Difference was $sizeDrop bytes.");
751
- $todel = $tempfile2;
752
- }
753
- } else {
754
- $this->debug(3, "pngcrush failed with output: $out");
755
- $todel = $tempfile2;
756
- }
757
- @unlink($todel);
758
- }
759
-
760
- $this->debug(3, "Rewriting image with security header.");
761
- $tempfile4 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
762
- $context = stream_context_create ();
763
- $fp = fopen($tempfile,'r',0,$context);
764
- file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>'); //6 extra bytes, first 3 being image type
765
- file_put_contents($tempfile4, $fp, FILE_APPEND);
766
- fclose($fp);
767
- @unlink($tempfile);
768
- $this->debug(3, "Locking and replacing cache file.");
769
- $lockFile = $this->cachefile . '.lock';
770
- $fh = fopen($lockFile, 'w');
771
- if(! $fh){
772
- return $this->error("Could not open the lockfile for writing an image.");
773
- }
774
- if(flock($fh, LOCK_EX)){
775
- @unlink($this->cachefile); //rename generally overwrites, but doing this in case of platform specific quirks. File might not exist yet.
776
- rename($tempfile4, $this->cachefile);
777
- flock($fh, LOCK_UN);
778
- fclose($fh);
779
- @unlink($lockFile);
780
- } else {
781
- fclose($fh);
782
- @unlink($lockFile);
783
- @unlink($tempfile4);
784
- return $this->error("Could not get a lock for writing.");
785
- }
786
- $this->debug(3, "Done image replace with security header. Cleaning up and running cleanCache()");
787
- imagedestroy($canvas);
788
- imagedestroy($image);
789
- return true;
790
- }
791
- protected function calcDocRoot(){
792
- $docRoot = @$_SERVER['DOCUMENT_ROOT'];
793
- if(!isset($docRoot)){
794
- $this->debug(3, "DOCUMENT_ROOT is not set. This is probably windows. Starting search 1.");
795
- if(isset($_SERVER['SCRIPT_FILENAME'])){
796
- $docRoot = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF'])));
797
- $this->debug(3, "Generated docRoot using SCRIPT_FILENAME and PHP_SELF as: $docRoot");
798
- }
799
- }
800
- if(!isset($docRoot)){
801
- $this->debug(3, "DOCUMENT_ROOT still is not set. Starting search 2.");
802
- if(isset($_SERVER['PATH_TRANSLATED'])){
803
- $docRoot = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF'])));
804
- $this->debug(3, "Generated docRoot using PATH_TRANSLATED and PHP_SELF as: $docRoot");
805
- }
806
- }
807
- if($docRoot){ $docRoot = preg_replace('/\/#x2F;', '', $docRoot); }
808
- $this->debug(3, "Doc root is: " . $docRoot);
809
- $this->docRoot = $docRoot;
810
-
811
- }
812
- protected function getLocalImagePath($src){
813
- $src = preg_replace('/^\//', '', $src); //strip off the leading '/'
814
- $realDocRoot = realpath($this->docRoot); //See issue 224. Using realpath as a windows fix.
815
- if(! $this->docRoot){
816
- $this->debug(3, "We have no document root set, so as a last resort, lets check if the image is in the current dir and serve that.");
817
- //We don't support serving images outside the current dir if we don't have a doc root for security reasons.
818
- $file = preg_replace('/^.*?([^\/\\\\]+)#x2F;', '$1', $src); //strip off any path info and just leave the filename.
819
- if(is_file($file)){
820
- return realpath($file);
821
- }
822
- return $this->error("Could not find your website document root and the file specified doesn't exist in timthumbs directory. We don't support serving files outside timthumb's directory without a document root for security reasons.");
823
- } //Do not go past this point without docRoot set
824
-
825
- //Try src under docRoot
826
- if(file_exists ($this->docRoot . '/' . $src)) {
827
- $this->debug(3, "Found file as " . $this->docRoot . '/' . $src);
828
- $real = realpath($this->docRoot . '/' . $src);
829
- if(strpos($real, $realDocRoot) === 0){
830
- return $real;
831
- } else {
832
- $this->debug(1, "Security block: The file specified occurs outside the document root.");
833
- //allow search to continue
834
- }
835
- }
836
- //Check absolute paths and then verify the real path is under doc root
837
- $absolute = realpath('/' . $src);
838
- if($absolute && file_exists($absolute)){ //realpath does file_exists check, so can probably skip the exists check here
839
- $this->debug(3, "Found absolute path: $absolute");
840
- if(! $this->docRoot){ $this->sanityFail("docRoot not set when checking absolute path."); }
841
- if(strpos($absolute, $realDocRoot) === 0){
842
- return $absolute;
843
- } else {
844
- $this->debug(1, "Security block: The file specified occurs outside the document root.");
845
- //and continue search
846
- }
847
- }
848
- $base = $this->docRoot;
849
- foreach (explode('/', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME'])) as $sub){
850
- $base .= $sub . '/';
851
- $this->debug(3, "Trying file as: " . $base . $src);
852
- if(file_exists($base . $src)){
853
- $this->debug(3, "Found file as: " . $base . $src);
854
- $real = realpath($base . $src);
855
- if(strpos($real, $realDocRoot) === 0){
856
- return $real;
857
- } else {
858
- $this->debug(1, "Security block: The file specified occurs outside the document root.");
859
- //And continue search
860
- }
861
- }
862
- }
863
- return false;
864
- }
865
- protected function toDelete($name){
866
- $this->debug(3, "Scheduling file $name to delete on destruct.");
867
- $this->toDeletes[] = $name;
868
- }
869
- protected function serveWebshot(){
870
- $this->debug(3, "Starting serveWebshot");
871
- $instr = "Please follow the instructions at http://code.google.com/p/timthumb/ to set your server up for taking website screenshots.";
872
- if(! is_file(WEBSHOT_CUTYCAPT)){
873
- return $this->error("CutyCapt is not installed. $instr");
874
- }
875
- if(! is_file(WEBSHOT_XVFB)){
876
- return $this->Error("Xvfb is not installed. $instr");
877
- }
878
- $cuty = WEBSHOT_CUTYCAPT;
879
- $xv = WEBSHOT_XVFB;
880
- $screenX = WEBSHOT_SCREEN_X;
881
- $screenY = WEBSHOT_SCREEN_Y;
882
- $colDepth = WEBSHOT_COLOR_DEPTH;
883
- $format = WEBSHOT_IMAGE_FORMAT;
884
- $timeout = WEBSHOT_TIMEOUT * 1000;
885
- $ua = WEBSHOT_USER_AGENT;
886
- $jsOn = WEBSHOT_JAVASCRIPT_ON ? 'on' : 'off';
887
- $javaOn = WEBSHOT_JAVA_ON ? 'on' : 'off';
888
- $pluginsOn = WEBSHOT_PLUGINS_ON ? 'on' : 'off';
889
- $proxy = WEBSHOT_PROXY ? ' --http-proxy=' . WEBSHOT_PROXY : '';
890
- $tempfile = tempnam($this->cacheDirectory, 'timthumb_webshot');
891
- $url = $this->src;
892
- if(! preg_match('/^https?:\/\/[a-zA-Z0-9\.\-]+/i', $url)){
893
- return $this->error("Invalid URL supplied.");
894
- }
895
- $url = preg_replace('/[^A-Za-z0-9\-\.\_\~:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=]+/', '', $url); //RFC 3986
896
- //Very important we don't allow injection of shell commands here. URL is between quotes and we are only allowing through chars allowed by a the RFC
897
- // which AFAIKT can't be used for shell injection.
898
- if(WEBSHOT_XVFB_RUNNING){
899
- putenv('DISPLAY=:100.0');
900
- $command = "$cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile";
901
- } else {
902
- $command = "$xv --server-args=\"-screen 0, {$screenX}x{$screenY}x{$colDepth}\" $cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile";
903
- }
904
- $this->debug(3, "Executing command: $command");
905
- $out = `$command`;
906
- $this->debug(3, "Received output: $out");
907
- if(! is_file($tempfile)){
908
- $this->set404();
909
- return $this->error("The command to create a thumbnail failed.");
910
- }
911
- $this->cropTop = true;
912
- if($this->processImageAndWriteToCache($tempfile)){
913
- $this->debug(3, "Image processed succesfully. Serving from cache");
914
- return $this->serveCacheFile();
915
- } else {
916
- return false;
917
- }
918
- }
919
- protected function serveExternalImage(){
920
- if(! preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+/i', $this->src)){
921
- $this->error("Invalid URL supplied.");
922
- return false;
923
- }
924
- $tempfile = tempnam($this->cacheDirectory, 'timthumb');
925
- $this->debug(3, "Fetching external image into temporary file $tempfile");
926
- $this->toDelete($tempfile);
927
- #fetch file here
928
- if(! $this->getURL($this->src, $tempfile)){
929
- @unlink($this->cachefile);
930
- touch($this->cachefile);
931
- $this->debug(3, "Error fetching URL: " . $this->lastURLError);
932
- $this->error("Error reading the URL you specified from remote host." . $this->lastURLError);
933
- return false;
934
- }
935
-
936
- $mimeType = $this->getMimeType($tempfile);
937
- if(! preg_match("/^image\/(?:jpg|jpeg|gif|png)#x2F;i", $mimeType)){
938
- $this->debug(3, "Remote file has invalid mime type: $mimeType");
939
- @unlink($this->cachefile);
940
- touch($this->cachefile);
941
- $this->error("The remote file is not a valid image.");
942
- return false;
943
- }
944
- if($this->processImageAndWriteToCache($tempfile)){
945
- $this->debug(3, "Image processed succesfully. Serving from cache");
946
- return $this->serveCacheFile();
947
- } else {
948
- return false;
949
- }
950
- }
951
- public static function curlWrite($h, $d){
952
- fwrite(self::$curlFH, $d);
953
- self::$curlDataWritten += strlen($d);
954
- if(self::$curlDataWritten > MAX_FILE_SIZE){
955
- return 0;
956
- } else {
957
- return strlen($d);
958
- }
959
- }
960
- protected function serveCacheFile(){
961
- $this->debug(3, "Serving {$this->cachefile}");
962
- if(! is_file($this->cachefile)){
963
- $this->error("serveCacheFile called in timthumb but we couldn't find the cached file.");
964
- return false;
965
- }
966
- $fp = fopen($this->cachefile, 'rb');
967
- if(! $fp){ return $this->error("Could not open cachefile."); }
968
- fseek($fp, strlen($this->filePrependSecurityBlock), SEEK_SET);
969
- $imgType = fread($fp, 3);
970
- fseek($fp, 3, SEEK_CUR);
971
- if(ftell($fp) != strlen($this->filePrependSecurityBlock) + 6){
972
- @unlink($this->cachefile);
973
- return $this->error("The cached image file seems to be corrupt.");
974
- }
975
- $imageDataSize = filesize($this->cachefile) - (strlen($this->filePrependSecurityBlock) + 6);
976
- $this->sendImageHeaders($imgType, $imageDataSize);
977
- $bytesSent = @fpassthru($fp);
978
- fclose($fp);
979
- if($bytesSent > 0){
980
- return true;
981
- }
982
- $content = file_get_contents ($this->cachefile);
983
- if ($content != FALSE) {
984
- $content = substr($content, strlen($this->filePrependSecurityBlock) + 6);
985
- echo $content;
986
- $this->debug(3, "Served using file_get_contents and echo");
987
- return true;
988
- } else {
989
- $this->error("Cache file could not be loaded.");
990
- return false;
991
- }
992
- }
993
- protected function sendImageHeaders($mimeType, $dataSize){
994
- if(! preg_match('/^image\//i', $mimeType)){
995
- $mimeType = 'image/' . $mimeType;
996
- }
997
- if(strtolower($mimeType) == 'image/jpg'){
998
- $mimeType = 'image/jpeg';
999
- }
1000
- $gmdate_expires = gmdate ('D, d M Y H:i:s', strtotime ('now +10 days')) . ' GMT';
1001
- $gmdate_modified = gmdate ('D, d M Y H:i:s') . ' GMT';
1002
- // send content headers then display image
1003
- header ('Content-Type: ' . $mimeType);
1004
- header ('Accept-Ranges: none'); //Changed this because we don't accept range requests
1005
- header ('Last-Modified: ' . $gmdate_modified);
1006
- header ('Content-Length: ' . $dataSize);
1007
- if(BROWSER_CACHE_DISABLE){
1008
- $this->debug(3, "Browser cache is disabled so setting non-caching headers.");
1009
- header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
1010
- header("Pragma: no-cache");
1011
- header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
1012
- } else {
1013
- $this->debug(3, "Browser caching is enabled");
1014
- header('Cache-Control: max-age=' . BROWSER_CACHE_MAX_AGE . ', must-revalidate');
1015
- header('Expires: ' . $gmdate_expires);
1016
- }
1017
- return true;
1018
- }
1019
- protected function securityChecks(){
1020
- }
1021
- protected function param($property, $default = ''){
1022
- if (isset ($_GET[$property])) {
1023
- return $_GET[$property];
1024
- } else {
1025
- return $default;
1026
- }
1027
- }
1028
- protected function openImage($mimeType, $src){
1029
- switch ($mimeType) {
1030
- case 'image/jpg': //This isn't a valid mime type so we should probably remove it
1031
- case 'image/jpeg':
1032
- $image = imagecreatefromjpeg ($src);
1033
- break;
1034
-
1035
- case 'image/png':
1036
- $image = imagecreatefrompng ($src);
1037
- break;
1038
-
1039
- case 'image/gif':
1040
- $image = imagecreatefromgif ($src);
1041
- break;
1042
- }
1043
-
1044
- return $image;
1045
- }
1046
- protected function getIP(){
1047
- $rem = @$_SERVER["REMOTE_ADDR"];
1048
- $ff = @$_SERVER["HTTP_X_FORWARDED_FOR"];
1049
- $ci = @$_SERVER["HTTP_CLIENT_IP"];
1050
- if(preg_match('/^(?:192\.168|172\.16|10\.|127\.)/', $rem)){
1051
- if($ff){ return $ff; }
1052
- if($ci){ return $ci; }
1053
- return $rem;
1054
- } else {
1055
- if($rem){ return $rem; }
1056
- if($ff){ return $ff; }
1057
- if($ci){ return $ci; }
1058
- return "UNKNOWN";
1059
- }
1060
- }
1061
- protected function debug($level, $msg){
1062
- if(DEBUG_ON && $level <= DEBUG_LEVEL){
1063
- $execTime = sprintf('%.6f', microtime(true) - $this->startTime);
1064
- $tick = sprintf('%.6f', 0);
1065
- if($this->lastBenchTime > 0){
1066
- $tick = sprintf('%.6f', microtime(true) - $this->lastBenchTime);
1067
- }
1068
- $this->lastBenchTime = microtime(true);
1069
- error_log("TimThumb Debug line " . __LINE__ . " [$execTime : $tick]: $msg");
1070
- }
1071
- }
1072
- protected function sanityFail($msg){
1073
- return $this->error("There is a problem in the timthumb code. Message: Please report this error at <a href='http://code.google.com/p/timthumb/issues/list'>timthumb's bug tracking page</a>: $msg");
1074
- }
1075
- protected function getMimeType($file){
1076
- $info = getimagesize($file);
1077
- if(is_array($info) && $info['mime']){
1078
- return $info['mime'];
1079
- }
1080
- return '';
1081
- }
1082
- protected function setMemoryLimit(){
1083
- $inimem = ini_get('memory_limit');
1084
- $inibytes = timthumb::returnBytes($inimem);
1085
- $ourbytes = timthumb::returnBytes(MEMORY_LIMIT);
1086
- if($inibytes < $ourbytes){
1087
- ini_set ('memory_limit', MEMORY_LIMIT);
1088
- $this->debug(3, "Increased memory from $inimem to " . MEMORY_LIMIT);
1089
- } else {
1090
- $this->debug(3, "Not adjusting memory size because the current setting is " . $inimem . " and our size of " . MEMORY_LIMIT . " is smaller.");
1091
- }
1092
- }
1093
- protected static function returnBytes($size_str){
1094
- switch (substr ($size_str, -1))
1095
- {
1096
- case 'M': case 'm': return (int)$size_str * 1048576;
1097
- case 'K': case 'k': return (int)$size_str * 1024;
1098
- case 'G': case 'g': return (int)$size_str * 1073741824;
1099
- default: return $size_str;
1100
- }
1101
- }
1102
- protected function getURL($url, $tempfile){
1103
- $this->lastURLError = false;
1104
- $url = preg_replace('/ /', '%20', $url);
1105
- if(function_exists('curl_init')){
1106
- $this->debug(3, "Curl is installed so using it to fetch URL.");
1107
- self::$curlFH = fopen($tempfile, 'w');
1108
- if(! self::$curlFH){
1109
- $this->error("Could not open $tempfile for writing.");
1110
- return false;
1111
- }
1112
- self::$curlDataWritten = 0;
1113
- $this->debug(3, "Fetching url with curl: $url");
1114
- $curl = curl_init($url);
1115
- curl_setopt ($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
1116
- curl_setopt ($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30");
1117
- curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
1118
- curl_setopt ($curl, CURLOPT_HEADER, 0);
1119
- curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
1120
- curl_setopt ($curl, CURLOPT_WRITEFUNCTION, 'timthumb::curlWrite');
1121
- @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
1122
- @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
1123
-
1124
- $curlResult = curl_exec($curl);
1125
- fclose(self::$curlFH);
1126
- $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
1127
- if($httpStatus == 404){
1128
- $this->set404();
1129
- }
1130
- if($curlResult){
1131
- curl_close($curl);
1132
- return true;
1133
- } else {
1134
- $this->lastURLError = curl_error($curl);
1135
- curl_close($curl);
1136
- return false;
1137
- }
1138
- } else {
1139
- $img = @file_get_contents ($url);
1140
- if($img === false){
1141
- $err = error_get_last();
1142
- if(is_array($err) && $err['message']){
1143
- $this->lastURLError = $err['message'];
1144
- } else {
1145
- $this->lastURLError = $err;
1146
- }
1147
- if(preg_match('/404/', $this->lastURLError)){
1148
- $this->set404();
1149
- }
1150
-
1151
- return false;
1152
- }
1153
- if(! file_put_contents($tempfile, $img)){
1154
- $this->error("Could not write to $tempfile.");
1155
- return false;
1156
- }
1157
- return true;
1158
- }
1159
-
1160
- }
1161
- protected function serveImg($file){
1162
- $s = getimagesize($file);
1163
- if(! ($s && $s['mime'])){
1164
- return false;
1165
- }
1166
- header ('Content-Type: ' . $s['mime']);
1167
- header ('Content-Length: ' . filesize($file) );
1168
- header ('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
1169
- header ("Pragma: no-cache");
1170
- $bytes = @readfile($file);
1171
- if($bytes > 0){
1172
- return true;
1173
- }
1174
- $content = @file_get_contents ($file);
1175
- if ($content != FALSE){
1176
- echo $content;
1177
- return true;
1178
- }
1179
- return false;
1180
-
1181
- }
1182
- protected function set404(){
1183
- $this->is404 = true;
1184
- }
1185
- protected function is404(){
1186
- return $this->is404;
1187
- }
1188
- }
1
+ <?php
2
+ /**
3
+ * TimThumb by Ben Gillbanks and Mark Maunder
4
+ * Based on work done by Tim McDaniels and Darren Hoyt
5
+ * http://code.google.com/p/timthumb/
6
+ *
7
+ * GNU General Public License, version 2
8
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9
+ *
10
+ * Examples and documentation available on the project homepage
11
+ * http://www.binarymoon.co.uk/projects/timthumb/
12
+ *
13
+ * $Rev$
14
+ */
15
+
16
+ /*
17
+ * --- TimThumb CONFIGURATION ---
18
+ * To edit the configs it is best to create a file called timthumb-config.php
19
+ * and define variables you want to customize in there. It will automatically be
20
+ * loaded by timthumb. This will save you having to re-edit these variables
21
+ * everytime you download a new version
22
+ */
23
+ define ('VERSION', '2.8.5'); // Version of this script
24
+ //Load a config file if it exists. Otherwise, use the values below
25
+ if( file_exists(dirname(__FILE__) . '/timthumb-config.php')) require_once('timthumb-config.php');
26
+ if(! defined('DEBUG_ON') ) define ('DEBUG_ON', false); // Enable debug logging to web server error log (STDERR)
27
+ if(! defined('DEBUG_LEVEL') ) define ('DEBUG_LEVEL', 1); // Debug level 1 is less noisy and 3 is the most noisy
28
+ if(! defined('MEMORY_LIMIT') ) define ('MEMORY_LIMIT', '30M'); // Set PHP memory limit
29
+ if(! defined('BLOCK_EXTERNAL_LEECHERS') ) define ('BLOCK_EXTERNAL_LEECHERS', false); // If the image or webshot is being loaded on an external site, display a red "No Hotlinking" gif.
30
+
31
+ //Image fetching and caching
32
+ if(! defined('ALLOW_EXTERNAL') ) define ('ALLOW_EXTERNAL', TRUE); // Allow image fetching from external websites. Will check against ALLOWED_SITES if ALLOW_ALL_EXTERNAL_SITES is false
33
+ if(! defined('ALLOW_ALL_EXTERNAL_SITES') ) define ('ALLOW_ALL_EXTERNAL_SITES', false); // Less secure.
34
+ if(! defined('FILE_CACHE_ENABLED') ) define ('FILE_CACHE_ENABLED', TRUE); // Should we store resized/modified images on disk to speed things up?
35
+ if(! defined('FILE_CACHE_TIME_BETWEEN_CLEANS')) define ('FILE_CACHE_TIME_BETWEEN_CLEANS', 86400); // How often the cache is cleaned
36
+
37
+ if(! defined('FILE_CACHE_MAX_FILE_AGE') ) define ('FILE_CACHE_MAX_FILE_AGE', 86400); // How old does a file have to be to be deleted from the cache
38
+ if(! defined('FILE_CACHE_SUFFIX') ) define ('FILE_CACHE_SUFFIX', '.timthumb.txt'); // What to put at the end of all files in the cache directory so we can identify them
39
+ if(! defined('FILE_CACHE_PREFIX') ) define ('FILE_CACHE_PREFIX', 'timthumb'); // What to put at the end of all files in the cache directory so we can identify them
40
+ if(! defined('FILE_CACHE_DIRECTORY') ) define ('FILE_CACHE_DIRECTORY', './cache'); // Directory where images are cached. Left blank it will use the system temporary directory (which is better for security)
41
+ if(! defined('MAX_FILE_SIZE') ) define ('MAX_FILE_SIZE', 10485760); // 10 Megs is 10485760. This is the max internal or external file size that we'll process.
42
+ if(! defined('CURL_TIMEOUT') ) define ('CURL_TIMEOUT', 20); // Timeout duration for Curl. This only applies if you have Curl installed and aren't using PHP's default URL fetching mechanism.
43
+ if(! defined('WAIT_BETWEEN_FETCH_ERRORS') ) define ('WAIT_BETWEEN_FETCH_ERRORS', 3600); //Time to wait between errors fetching remote file
44
+
45
+ //Browser caching
46
+ if(! defined('BROWSER_CACHE_MAX_AGE') ) define ('BROWSER_CACHE_MAX_AGE', 864000); // Time to cache in the browser
47
+ if(! defined('BROWSER_CACHE_DISABLE') ) define ('BROWSER_CACHE_DISABLE', false); // Use for testing if you want to disable all browser caching
48
+
49
+ //Image size and defaults
50
+ if(! defined('MAX_WIDTH') ) define ('MAX_WIDTH', 1500); // Maximum image width
51
+ if(! defined('MAX_HEIGHT') ) define ('MAX_HEIGHT', 1500); // Maximum image height
52
+ if(! defined('NOT_FOUND_IMAGE') ) define ('NOT_FOUND_IMAGE', ''); // Image to serve if any 404 occurs
53
+ if(! defined('ERROR_IMAGE') ) define ('ERROR_IMAGE', ''); // Image to serve if an error occurs instead of showing error message
54
+ if(! defined('DEFAULT_Q') ) define ('DEFAULT_Q', 90); // Default image quality. Allows overrid in timthumb-config.php
55
+ if(! defined('DEFAULT_ZC') ) define ('DEFAULT_ZC', 1); // Default zoom/crop setting. Allows overrid in timthumb-config.php
56
+ if(! defined('DEFAULT_F') ) define ('DEFAULT_F', ''); // Default image filters. Allows overrid in timthumb-config.php
57
+ if(! defined('DEFAULT_S') ) define ('DEFAULT_S', 0); // Default sharpen value. Allows overrid in timthumb-config.php
58
+ if(! defined('DEFAULT_CC') ) define ('DEFAULT_CC', 'ffffff'); // Default canvas colour. Allows overrid in timthumb-config.php
59
+
60
+
61
+ //Image compression is enabled if either of these point to valid paths
62
+
63
+ //These are now disabled by default because the file sizes of PNGs (and GIFs) are much smaller than we used to generate.
64
+ //They only work for PNGs. GIFs and JPEGs are not affected.
65
+ if(! defined('OPTIPNG_ENABLED') ) define ('OPTIPNG_ENABLED', false);
66
+ if(! defined('OPTIPNG_PATH') ) define ('OPTIPNG_PATH', '/usr/bin/optipng'); //This will run first because it gives better compression than pngcrush.
67
+ if(! defined('PNGCRUSH_ENABLED') ) define ('PNGCRUSH_ENABLED', false);
68
+ if(! defined('PNGCRUSH_PATH') ) define ('PNGCRUSH_PATH', '/usr/bin/pngcrush'); //This will only run if OPTIPNG_PATH is not set or is not valid
69
+
70
+ /*
71
+ -------====Website Screenshots configuration - BETA====-------
72
+
73
+ If you just want image thumbnails and don't want website screenshots, you can safely leave this as is.
74
+
75
+ If you would like to get website screenshots set up, you will need root access to your own server.
76
+
77
+ Enable ALLOW_ALL_EXTERNAL_SITES so you can fetch any external web page. This is more secure now that we're using a non-web folder for cache.
78
+ Enable BLOCK_EXTERNAL_LEECHERS so that your site doesn't generate thumbnails for the whole Internet.
79
+
80
+ Instructions to get website screenshots enabled on Ubuntu Linux:
81
+
82
+ 1. Install Xvfb with the following command: sudo apt-get install subversion libqt4-webkit libqt4-dev g++ xvfb
83
+ 2. Go to a directory where you can download some code
84
+ 3. Check-out the latest version of CutyCapt with the following command: svn co https://cutycapt.svn.sourceforge.net/svnroot/cutycapt
85
+ 4. Compile CutyCapt by doing: cd cutycapt/CutyCapt
86
+ 5. qmake
87
+ 6. make
88
+ 7. cp CutyCapt /usr/local/bin/
89
+ 8. Test it by running: xvfb-run --server-args="-screen 0, 1024x768x24" CutyCapt --url="http://markmaunder.com/" --out=test.png
90
+ 9. If you get a file called test.png with something in it, it probably worked. Now test the script by accessing it as follows:
91
+ 10. http://yoursite.com/path/to/timthumb.php?src=http://markmaunder.com/&webshot=1
92
+
93
+ Notes on performance:
94
+ The first time a webshot loads, it will take a few seconds.
95
+ From then on it uses the regular timthumb caching mechanism with the configurable options above
96
+ and loading will be very fast.
97
+
98
+ --ADVANCED USERS ONLY--
99
+ If you'd like a slight speedup (about 25%) and you know Linux, you can run the following command which will keep Xvfb running in the background.
100
+ nohup Xvfb :100 -ac -nolisten tcp -screen 0, 1024x768x24 > /dev/null 2>&1 &
101
+ Then set WEBSHOT_XVFB_RUNNING = true below. This will save your server having to fire off a new Xvfb server and shut it down every time a new shot is generated.
102
+ You will need to take responsibility for keeping Xvfb running in case it crashes. (It seems pretty stable)
103
+ You will also need to take responsibility for server security if you're running Xvfb as root.
104
+
105
+
106
+ */
107
+ if(! defined('WEBSHOT_ENABLED') ) define ('WEBSHOT_ENABLED', false); //Beta feature. Adding webshot=1 to your query string will cause the script to return a browser screenshot rather than try to fetch an image.
108
+ if(! defined('WEBSHOT_CUTYCAPT') ) define ('WEBSHOT_CUTYCAPT', '/usr/local/bin/CutyCapt'); //The path to CutyCapt.
109
+ if(! defined('WEBSHOT_XVFB') ) define ('WEBSHOT_XVFB', '/usr/bin/xvfb-run'); //The path to the Xvfb server
110
+ if(! defined('WEBSHOT_SCREEN_X') ) define ('WEBSHOT_SCREEN_X', '1024'); //1024 works ok
111
+ if(! defined('WEBSHOT_SCREEN_Y') ) define ('WEBSHOT_SCREEN_Y', '768'); //768 works ok
112
+ if(! defined('WEBSHOT_COLOR_DEPTH') ) define ('WEBSHOT_COLOR_DEPTH', '24'); //I haven't tested anything besides 24
113
+ if(! defined('WEBSHOT_IMAGE_FORMAT') ) define ('WEBSHOT_IMAGE_FORMAT', 'png'); //png is about 2.5 times the size of jpg but is a LOT better quality
114
+ if(! defined('WEBSHOT_TIMEOUT') ) define ('WEBSHOT_TIMEOUT', '20'); //Seconds to wait for a webshot
115
+ if(! defined('WEBSHOT_USER_AGENT') ) define ('WEBSHOT_USER_AGENT', "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"); //I hate to do this, but a non-browser robot user agent might not show what humans see. So we pretend to be Firefox
116
+ if(! defined('WEBSHOT_JAVASCRIPT_ON') ) define ('WEBSHOT_JAVASCRIPT_ON', true); //Setting to false might give you a slight speedup and block ads. But it could cause other issues.
117
+ if(! defined('WEBSHOT_JAVA_ON') ) define ('WEBSHOT_JAVA_ON', false); //Have only tested this as fase
118
+ if(! defined('WEBSHOT_PLUGINS_ON') ) define ('WEBSHOT_PLUGINS_ON', true); //Enable flash and other plugins
119
+ if(! defined('WEBSHOT_PROXY') ) define ('WEBSHOT_PROXY', ''); //In case you're behind a proxy server.
120
+ if(! defined('WEBSHOT_XVFB_RUNNING') ) define ('WEBSHOT_XVFB_RUNNING', false); //ADVANCED: Enable this if you've got Xvfb running in the background.
121
+
122
+
123
+ // If ALLOW_EXTERNAL is true and ALLOW_ALL_EXTERNAL_SITES is false, then external images will only be fetched from these domains and their subdomains.
124
+ if(! isset($ALLOWED_SITES)){
125
+ $ALLOWED_SITES = array (
126
+ 'flickr.com',
127
+ 'staticflickr.com',
128
+ 'picasa.com',
129
+ 'img.youtube.com',
130
+ 'upload.wikimedia.org',
131
+ 'photobucket.com',
132
+ 'imgur.com',
133
+ 'imageshack.us',
134
+ 'tinypic.com',
135
+ );
136
+ }
137
+ // -------------------------------------------------------------
138
+ // -------------- STOP EDITING CONFIGURATION HERE --------------
139
+ // -------------------------------------------------------------
140
+
141
+ timthumb::start();
142
+
143
+ class timthumb {
144
+ protected $src = "";
145
+ protected $is404 = false;
146
+ protected $docRoot = "";
147
+ protected $lastURLError = false;
148
+ protected $localImage = "";
149
+ protected $localImageMTime = 0;
150
+ protected $url = false;
151
+ protected $myHost = "";
152
+ protected $isURL = false;
153
+ protected $cachefile = '';
154
+ protected $errors = array();
155
+ protected $toDeletes = array();
156
+ protected $cacheDirectory = '';
157
+ protected $startTime = 0;
158
+ protected $lastBenchTime = 0;
159
+ protected $cropTop = false;
160
+ protected $salt = "";
161
+ protected $fileCacheVersion = 1; //Generally if timthumb.php is modifed (upgraded) then the salt changes and all cache files are recreated. This is a backup mechanism to force regen.
162
+ protected $filePrependSecurityBlock = "<?php die('Execution denied!'); //"; //Designed to have three letter mime type, space, question mark and greater than symbol appended. 6 bytes total.
163
+ protected static $curlDataWritten = 0;
164
+ protected static $curlFH = false;
165
+ public static function start(){
166
+ $tim = new timthumb();
167
+ $tim->handleErrors();
168
+ $tim->securityChecks();
169
+ if($tim->tryBrowserCache()){
170
+ exit(0);
171
+ }
172
+ $tim->handleErrors();
173
+ if(FILE_CACHE_ENABLED && $tim->tryServerCache()){
174
+ exit(0);
175
+ }
176
+ $tim->handleErrors();
177
+ $tim->run();
178
+ $tim->handleErrors();
179
+ exit(0);
180
+ }
181
+ public function __construct(){
182
+ global $ALLOWED_SITES;
183
+ $this->startTime = microtime(true);
184
+ date_default_timezone_set('UTC');
185
+ $this->debug(1, "Starting new request from " . $this->getIP() . " to " . $_SERVER['REQUEST_URI']);
186
+ $this->calcDocRoot();
187
+ //On windows systems I'm assuming fileinode returns an empty string or a number that doesn't change. Check this.
188
+ $this->salt = @filemtime(__FILE__) . '-' . @fileinode(__FILE__);
189
+ $this->debug(3, "Salt is: " . $this->salt);
190
+ if(FILE_CACHE_DIRECTORY){
191
+ if(! is_dir(FILE_CACHE_DIRECTORY)){
192
+ @mkdir(FILE_CACHE_DIRECTORY);
193
+ if(! is_dir(FILE_CACHE_DIRECTORY)){
194
+ $this->error("Could not create the file cache directory.");