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}$/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}$/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 . '$/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)$/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)$/i', $mimeType)){
710
- $imgType = 'jpg';
711
- imagejpeg($canvas, $tempfile, $quality);
712
- } else if(preg_match('/^image\/png$/i', $mimeType)){
713
- $imgType = 'png';
714
- imagepng($canvas, $tempfile, floor($quality * 0.09));
715
- } else if(preg_match('/^image\/gif$/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('/\/$/', '', $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('/^.*?([^\/\\\\]+)$/', '$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)$/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.");
195
+ return false;
196
+ }
197
+ }
198
+ $this->cacheDirectory = FILE_CACHE_DIRECTORY;
199
+ if (!touch($this->cacheDirectory . '/index.html')) {
200
+ $this->error("Could note create the index.html file - to fix this create an empty file named index.html file in the cache directory.");
201
+ }
202
+ } else {
203
+ $this->cacheDirectory = sys_get_temp_dir();
204
+ }
205
+ //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.
206
+ $this->cleanCache();
207
+
208
+ $this->myHost = preg_replace('/^www\./i', '', $_SERVER['HTTP_HOST']);
209
+ $this->src = $this->param('src');
210
+ $this->url = parse_url($this->src);
211
+ if(strlen($this->src) <= 3){
212
+ $this->error("No image specified");
213
+ return false;
214
+ }
215
+ if(BLOCK_EXTERNAL_LEECHERS && array_key_exists('HTTP_REFERER', $_SERVER) && (! preg_match('/^https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $_SERVER['HTTP_REFERER']))){
216
+ // base64 encoded red image that says 'no hotlinkers'
217
+ // nothing to worry about! :)
218
+ $imgData = base64_decode("R0lGODlhUAAMAIAAAP8AAP///yH5BAAHAP8ALAAAAABQAAwAAAJpjI+py+0Po5y0OgAMjjv01YUZ\nOGplhWXfNa6JCLnWkXplrcBmW+spbwvaVr/cDyg7IoFC2KbYVC2NQ5MQ4ZNao9Ynzjl9ScNYpneb\nDULB3RP6JuPuaGfuuV4fumf8PuvqFyhYtjdoeFgAADs=");
219
+ header('Content-Type: image/gif');
220
+ header('Content-Length: ' . sizeof($imgData));
221
+ header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
222
+ header("Pragma: no-cache");
223
+ header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
224
+ echo $imgData;
225
+ return false;
226
+ exit(0);
227
+ }
228
+ if(preg_match('/https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $this->src)){
229
+ $this->src = preg_replace('/https?:\/\/(?:www\.)?' . $this->myHost . '/i', '', $this->src);
230
+ }
231
+ if(preg_match('/^https?:\/\/[^\/]+/i', $this->src)){
232
+ $this->debug(2, "Is a request for an external URL: " . $this->src);
233
+ $this->isURL = true;
234
+ } else {
235
+ $this->debug(2, "Is a request for an internal file: " . $this->src);
236
+ }
237
+ if($this->isURL && (! ALLOW_EXTERNAL)){
238
+ $this->error("You are not allowed to fetch images from an external website.");
239
+ return false;
240
+ }
241
+ if($this->isURL){
242
+ if(ALLOW_ALL_EXTERNAL_SITES){
243
+ $this->debug(2, "Fetching from all external sites is enabled.");
244
+ } else {
245
+ $this->debug(2, "Fetching only from selected external sites is enabled.");
246
+ $allowed = false;
247
+ foreach($ALLOWED_SITES as $site){
248
+ if ((strtolower(substr($this->url['host'],-strlen($site)-1)) === strtolower(".$site")) || (strtolower($this->url['host'])===strtolower($site))) {
249
+ $this->debug(3, "URL hostname {$this->url['host']} matches $site so allowing.");
250
+ $allowed = true;
251
+ }
252
+ }
253
+ if(! $allowed){
254
+ 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.");
255
+ }
256
+ }
257
+ }
258
+
259
+ $cachePrefix = ($this->isURL ? '_ext_' : '_int_');
260
+ if($this->isURL){
261
+ $arr = explode('&', $_SERVER ['QUERY_STRING']);
262
+ asort($arr);
263
+ $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . implode('', $arr) . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
264
+ } else {
265
+ $this->localImage = $this->getLocalImagePath($this->src);
266
+ if(! $this->localImage){
267
+ $this->debug(1, "Could not find the local image: {$this->localImage}");
268
+ $this->error("Could not find the internal image you specified.");
269
+ $this->set404();
270
+ return false;
271
+ }
272
+ $this->debug(1, "Local image path is {$this->localImage}");
273
+ $this->localImageMTime = @filemtime($this->localImage);
274
+ //We include the mtime of the local file in case in changes on disk.
275
+ $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . $this->localImageMTime . $_SERVER ['QUERY_STRING'] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
276
+ }
277
+ $this->debug(2, "Cache file is: " . $this->cachefile);
278
+
279
+ return true;
280
+ }
281
+ public function __destruct(){
282
+ foreach($this->toDeletes as $del){
283
+ $this->debug(2, "Deleting temp file $del");
284
+ @unlink($del);
285
+ }
286
+ }
287
+ public function run(){
288
+ if($this->isURL){
289
+ if(! ALLOW_EXTERNAL){
290
+ $this->debug(1, "Got a request for an external image but ALLOW_EXTERNAL is disabled so returning error msg.");
291
+ $this->error("You are not allowed to fetch images from an external website.");
292
+ return false;
293
+ }
294
+ $this->debug(3, "Got request for external image. Starting serveExternalImage.");
295
+ if($this->param('webshot')){
296
+ if(WEBSHOT_ENABLED){
297
+ $this->debug(3, "webshot param is set, so we're going to take a webshot.");
298
+ $this->serveWebshot();
299
+ } else {
300
+ $this->error("You added the webshot parameter but webshots are disabled on this server. You need to set WEBSHOT_ENABLED == true to enable webshots.");
301
+ }
302
+ } else {
303
+ $this->debug(3, "webshot is NOT set so we're going to try to fetch a regular image.");
304
+ $this->serveExternalImage();
305
+
306
+ }
307
+ } else {
308
+ $this->debug(3, "Got request for internal image. Starting serveInternalImage()");
309
+ $this->serveInternalImage();
310
+ }
311
+ return true;
312
+ }
313
+ protected function handleErrors(){
314
+ if($this->haveErrors()){
315
+ if(NOT_FOUND_IMAGE && $this->is404()){
316
+ if($this->serveImg(NOT_FOUND_IMAGE)){
317
+ exit(0);
318
+ } else {
319
+ $this->error("Additionally, the 404 image that is configured could not be found or there was an error serving it.");
320
+ }
321
+ }
322
+ if(ERROR_IMAGE){
323
+ if($this->serveImg(ERROR_IMAGE)){
324
+ exit(0);
325
+ } else {
326
+ $this->error("Additionally, the error image that is configured could not be found or there was an error serving it.");
327
+ }
328
+ }
329
+
330
+ $this->serveErrors();
331
+ exit(0);
332
+ }
333
+ return false;
334
+ }
335
+ protected function tryBrowserCache(){
336
+ if(BROWSER_CACHE_DISABLE){ $this->debug(3, "Browser caching is disabled"); return false; }
337
+ if(!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ){
338
+ $this->debug(3, "Got a conditional get");
339
+ $mtime = false;
340
+ //We've already checked if the real file exists in the constructor
341
+ if(! is_file($this->cachefile)){
342
+ //If we don't have something cached, regenerate the cached image.
343
+ return false;
344
+ }
345
+ if($this->localImageMTime){
346
+ $mtime = $this->localImageMTime;
347
+ $this->debug(3, "Local real file's modification time is $mtime");
348
+ } 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
349
+ $mtime = @filemtime($this->cachefile);
350
+ $this->debug(3, "Cached file's modification time is $mtime");
351
+ }
352
+ if(! $mtime){ return false; }
353
+
354
+ $iftime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
355
+ $this->debug(3, "The conditional get's if-modified-since unixtime is $iftime");
356
+ if($iftime < 1){
357
+ $this->debug(3, "Got an invalid conditional get modified since time. Returning false.");
358
+ return false;
359
+ }
360
+ if($iftime < $mtime){ //Real file or cache file has been modified since last request, so force refetch.
361
+ $this->debug(3, "File has been modified since last fetch.");
362
+ return false;
363
+ } else { //Otherwise serve a 304
364
+ $this->debug(3, "File has not been modified since last get, so serving a 304.");
365
+ header ($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
366
+ $this->debug(1, "Returning 304 not modified");
367
+ return true;
368
+ }
369
+ }
370
+ return false;
371
+ }
372
+ protected function tryServerCache(){
373
+ $this->debug(3, "Trying server cache");
374
+ if(file_exists($this->cachefile)){
375
+ $this->debug(3, "Cachefile {$this->cachefile} exists");
376
+ if($this->isURL){
377
+ $this->debug(3, "This is an external request, so checking if the cachefile is empty which means the request failed previously.");
378
+ if(filesize($this->cachefile) < 1){
379
+ $this->debug(3, "Found an empty cachefile indicating a failed earlier request. Checking how old it is.");
380
+ //Fetching error occured previously
381
+ if(time() - @filemtime($this->cachefile) > WAIT_BETWEEN_FETCH_ERRORS){
382
+ $this->debug(3, "File is older than " . WAIT_BETWEEN_FETCH_ERRORS . " seconds. Deleting and returning false so app can try and load file.");
383
+ @unlink($this->cachefile);
384
+ return false; //to indicate we didn't serve from cache and app should try and load
385
+ } else {
386
+ $this->debug(3, "Empty cachefile is still fresh so returning message saying we had an error fetching this image from remote host.");
387
+ $this->set404();
388
+ $this->error("An error occured fetching image.");
389
+ return false;
390
+ }
391
+ }
392
+ } else {
393
+ $this->debug(3, "Trying to serve cachefile {$this->cachefile}");
394
+ }
395
+ if($this->serveCacheFile()){
396
+ $this->debug(3, "Succesfully served cachefile {$this->cachefile}");
397
+ return true;
398
+ } else {
399
+ $this->debug(3, "Failed to serve cachefile {$this->cachefile} - Deleting it from cache.");
400
+ //Image serving failed. We can't retry at this point, but lets remove it from cache so the next request recreates it
401
+ @unlink($this->cachefile);
402
+ return true;
403
+ }
404
+ }
405
+ }
406
+ protected function error($err){
407
+ $this->debug(3, "Adding error message: $err");
408
+ $this->errors[] = $err;
409
+ return false;
410
+
411
+ }
412
+ protected function haveErrors(){
413
+ if(sizeof($this->errors) > 0){
414
+ return true;
415
+ }
416
+ return false;
417
+ }
418
+ protected function serveErrors(){
419
+ $html = '<ul>';
420
+ foreach($this->errors as $err){
421
+ $html .= '<li>' . htmlentities($err) . '</li>';
422
+ }
423
+ $html .= '</ul>';
424
+ header ($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
425
+ echo '<h1>A TimThumb error has occured</h1>The following error(s) occured:<br />' . $html . '<br />';
426
+ echo '<br />Query String : ' . htmlentities ($_SERVER['QUERY_STRING']);
427
+ echo '<br />TimThumb version : ' . VERSION . '</pre>';
428
+ }
429
+ protected function serveInternalImage(){
430
+ $this->debug(3, "Local image path is $this->localImage");
431
+ if(! $this->localImage){
432
+ $this->sanityFail("localImage not set after verifying it earlier in the code.");
433
+ return false;
434
+ }
435
+ $fileSize = filesize($this->localImage);
436
+ if($fileSize > MAX_FILE_SIZE){
437
+ $this->error("The file you specified is greater than the maximum allowed file size.");
438
+ return false;
439
+ }
440
+ if($fileSize <= 0){
441
+ $this->error("The file you specified is <= 0 bytes.");
442
+ return false;
443
+ }
444
+ $this->debug(3, "Calling processImageAndWriteToCache() for local image.");
445
+ if($this->processImageAndWriteToCache($this->localImage)){
446
+ $this->serveCacheFile();
447
+ return true;
448
+ } else {
449
+ return false;
450
+ }
451
+ }
452
+ protected function cleanCache(){
453
+ if (FILE_CACHE_TIME_BETWEEN_CLEANS < 0) {
454
+ return;
455
+ }
456
+ $this->debug(3, "cleanCache() called");
457
+ $lastCleanFile = $this->cacheDirectory . '/timthumb_cacheLastCleanTime.touch';
458
+
459
+ //If this is a new timthumb installation we need to create the file
460
+ if(! is_file($lastCleanFile)){
461
+ $this->debug(1, "File tracking last clean doesn't exist. Creating $lastCleanFile");
462
+ if (!touch($lastCleanFile)) {
463
+ $this->error("Could note create cache clean timestamp file.");
464
+ }
465
+ return;
466
+ }
467
+ if(@filemtime($lastCleanFile) < (time() - FILE_CACHE_TIME_BETWEEN_CLEANS) ){ //Cache was last cleaned more than 1 day ago
468
+ $this->debug(1, "Cache was last cleaned more than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago. Cleaning now.");
469
+ // Very slight race condition here, but worst case we'll have 2 or 3 servers cleaning the cache simultaneously once a day.
470
+ if (!touch($lastCleanFile)) {
471
+ $this->error("Could note create cache clean timestamp file.");
472
+ }
473
+ $files = glob($this->cacheDirectory . '/*' . FILE_CACHE_SUFFIX);
474
+ $timeAgo = time() - FILE_CACHE_MAX_FILE_AGE;
475
+ foreach($files as $file){
476
+ if(@filemtime($file) < $timeAgo){
477
+ $this->debug(3, "Deleting cache file $file older than max age: " . FILE_CACHE_MAX_FILE_AGE . " seconds");
478
+ @unlink($file);
479
+ }
480
+ }
481
+ return true;
482
+ } else {
483
+ $this->debug(3, "Cache was cleaned less than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago so no cleaning needed.");
484
+ }
485
+ return false;
486
+ }
487
+ protected function processImageAndWriteToCache($localImage){
488
+ $sData = getimagesize($localImage);
489
+ $origType = $sData[2];
490
+ $mimeType = $sData['mime'];
491
+
492
+ $this->debug(3, "Mime type of image is $mimeType");
493
+ if(! preg_match('/^image\/(?:gif|jpg|jpeg|png)$/i', $mimeType)){
494
+ return $this->error("The image being resized is not a valid gif, jpg or png.");
495
+ }
496
+
497
+ if (!function_exists ('imagecreatetruecolor')) {
498
+ return $this->error('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library');
499
+ }
500
+
501
+ if (function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
502
+ $imageFilters = array (
503
+ 1 => array (IMG_FILTER_NEGATE, 0),
504
+ 2 => array (IMG_FILTER_GRAYSCALE, 0),
505
+ 3 => array (IMG_FILTER_BRIGHTNESS, 1),
506
+ 4 => array (IMG_FILTER_CONTRAST, 1),
507
+ 5 => array (IMG_FILTER_COLORIZE, 4),
508
+ 6 => array (IMG_FILTER_EDGEDETECT, 0),
509
+ 7 => array (IMG_FILTER_EMBOSS, 0),
510
+ 8 => array (IMG_FILTER_GAUSSIAN_BLUR, 0),
511
+ 9 => array (IMG_FILTER_SELECTIVE_BLUR, 0),
512
+ 10 => array (IMG_FILTER_MEAN_REMOVAL, 0),
513
+ 11 => array (IMG_FILTER_SMOOTH, 0),
514
+ );
515
+ }
516
+
517
+ // get standard input properties
518
+ $new_width = (int) abs ($this->param('w', 0));
519
+ $new_height = (int) abs ($this->param('h', 0));
520
+ $zoom_crop = (int) $this->param('zc', DEFAULT_ZC);
521
+ $quality = (int) abs ($this->param('q', DEFAULT_Q));
522
+ $align = $this->cropTop ? 't' : $this->param('a', 'c');
523
+ $filters = $this->param('f', DEFAULT_F);
524
+ $sharpen = (bool) $this->param('s', DEFAULT_S);
525
+ $canvas_color = $this->param('cc', DEFAULT_CC);
526
+
527
+ // set default width and height if neither are set already
528
+ if ($new_width == 0 && $new_height == 0) {
529
+ $new_width = 100;
530
+ $new_height = 100;
531
+ }
532
+
533
+ // ensure size limits can not be abused
534
+ $new_width = min ($new_width, MAX_WIDTH);
535
+ $new_height = min ($new_height, MAX_HEIGHT);
536
+
537
+ // set memory limit to be able to have enough space to resize larger images
538
+ $this->setMemoryLimit();
539
+
540
+ // open the existing image
541
+ $image = $this->openImage ($mimeType, $localImage);
542
+ if ($image === false) {
543
+ return $this->error('Unable to open image.');
544
+ }
545
+
546
+ // Get original width and height
547
+ $width = imagesx ($image);
548
+ $height = imagesy ($image);
549
+ $origin_x = 0;
550
+ $origin_y = 0;
551
+
552
+ // generate new w/h if not provided
553
+ if ($new_width && !$new_height) {
554
+ $new_height = floor ($height * ($new_width / $width));
555
+ } else if ($new_height && !$new_width) {
556
+ $new_width = floor ($width * ($new_height / $height));
557
+ }
558
+
559
+ // scale down and add borders
560
+ if ($zoom_crop == 3) {
561
+
562
+ $final_height = $height * ($new_width / $width);
563
+
564
+ if ($final_height > $new_height) {
565
+ $new_width = $width * ($new_height / $height);
566
+ } else {
567
+ $new_height = $final_height;
568
+ }
569
+
570
+ }
571
+
572
+ // create a new true color image
573
+ $canvas = imagecreatetruecolor ($new_width, $new_height);
574
+ imagealphablending ($canvas, false);
575
+
576
+ if (strlen ($canvas_color) < 6) {
577
+ $canvas_color = 'ffffff';
578
+ }
579
+
580
+ $canvas_color_R = hexdec (substr ($canvas_color, 0, 2));
581
+ $canvas_color_G = hexdec (substr ($canvas_color, 2, 2));
582
+ $canvas_color_B = hexdec (substr ($canvas_color, 2, 2));
583
+
584
+ // Create a new transparent color for image
585
+ $color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 127);
586
+
587
+ // Completely fill the background of the new image with allocated color.
588
+ imagefill ($canvas, 0, 0, $color);
589
+
590
+ // scale down and add borders
591
+ if ($zoom_crop == 2) {
592
+
593
+ $final_height = $height * ($new_width / $width);
594
+
595
+ if ($final_height > $new_height) {
596
+
597
+ $origin_x = $new_width / 2;
598
+ $new_width = $width * ($new_height / $height);
599
+ $origin_x = round ($origin_x - ($new_width / 2));
600
+
601
+ } else {
602
+
603
+ $origin_y = $new_height / 2;
604
+ $new_height = $final_height;
605
+ $origin_y = round ($origin_y - ($new_height / 2));
606
+
607
+ }
608
+
609
+ }
610
+
611
+ // Restore transparency blending
612
+ imagesavealpha ($canvas, true);
613
+
614
+ if ($zoom_crop > 0) {
615
+
616
+ $src_x = $src_y = 0;
617
+ $src_w = $width;
618
+ $src_h = $height;
619
+
620
+ $cmp_x = $width / $new_width;
621
+ $cmp_y = $height / $new_height;
622
+
623
+ // calculate x or y coordinate and width or height of source
624
+ if ($cmp_x > $cmp_y) {
625
+
626
+ $src_w = round ($width / $cmp_x * $cmp_y);
627
+ $src_x = round (($width - ($width / $cmp_x * $cmp_y)) / 2);
628
+
629
+ } else if ($cmp_y > $cmp_x) {
630
+
631
+ $src_h = round ($height / $cmp_y * $cmp_x);
632
+ $src_y = round (($height - ($height / $cmp_y * $cmp_x)) / 2);
633
+
634
+ }
635
+
636
+ // positional cropping!
637
+ if ($align) {
638
+ if (strpos ($align, 't') !== false) {
639
+ $src_y = 0;
640
+ }
641
+ if (strpos ($align, 'b') !== false) {
642
+ $src_y = $height - $src_h;
643
+ }
644
+ if (strpos ($align, 'l') !== false) {
645
+ $src_x = 0;
646
+ }
647
+ if (strpos ($align, 'r') !== false) {
648
+ $src_x = $width - $src_w;
649
+ }
650
+ }
651
+
652
+ imagecopyresampled ($canvas, $image, $origin_x, $origin_y, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h);
653
+
654
+ } else {
655
+
656
+ // copy and resize part of an image with resampling
657
+ imagecopyresampled ($canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
658
+
659
+ }
660
+
661
+ if ($filters != '' && function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
662
+ // apply filters to image
663
+ $filterList = explode ('|', $filters);
664
+ foreach ($filterList as $fl) {
665
+
666
+ $filterSettings = explode (',', $fl);
667
+ if (isset ($imageFilters[$filterSettings[0]])) {
668
+
669
+ for ($i = 0; $i < 4; $i ++) {
670
+ if (!isset ($filterSettings[$i])) {
671
+ $filterSettings[$i] = null;
672
+ } else {
673
+ $filterSettings[$i] = (int) $filterSettings[$i];
674
+ }
675
+ }
676
+
677
+ switch ($imageFilters[$filterSettings[0]][1]) {
678
+
679
+ case 1:
680
+
681
+ imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]);
682
+ break;
683
+
684
+ case 2:
685
+
686
+ imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]);
687
+ break;
688
+
689
+ case 3:
690
+
691
+ imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]);
692
+ break;
693
+
694
+ case 4:
695
+
696
+ imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3], $filterSettings[4]);
697
+ break;
698
+
699
+ default:
700
+
701
+ imagefilter ($canvas, $imageFilters[$filterSettings[0]][0]);
702
+ break;
703
+
704
+ }
705
+ }
706
+ }
707
+ }
708
+
709
+ // sharpen image
710
+ if ($sharpen && function_exists ('imageconvolution')) {
711
+
712
+ $sharpenMatrix = array (
713
+ array (-1,-1,-1),
714
+ array (-1,16,-1),
715
+ array (-1,-1,-1),
716
+ );
717
+
718
+ $divisor = 8;
719
+ $offset = 0;
720
+
721
+ imageconvolution ($canvas, $sharpenMatrix, $divisor, $offset);
722
+
723
+ }
724
+ //Straight from Wordpress core code. Reduces filesize by up to 70% for PNG's
725
+ if ( (IMAGETYPE_PNG == $origType || IMAGETYPE_GIF == $origType) && function_exists('imageistruecolor') && !imageistruecolor( $image ) && imagecolortransparent( $image ) > 0 ){
726
+ imagetruecolortopalette( $canvas, false, imagecolorstotal( $image ) );
727
+ }
728
+
729
+ $imgType = "";
730
+ $tempfile = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
731
+ if(preg_match('/^image\/(?:jpg|jpeg)$/i', $mimeType)){
732
+ $imgType = 'jpg';
733
+ imagejpeg($canvas, $tempfile, $quality);
734
+ } else if(preg_match('/^image\/png$/i', $mimeType)){
735
+ $imgType = 'png';
736
+ imagepng($canvas, $tempfile, floor($quality * 0.09));
737
+ } else if(preg_match('/^image\/gif$/i', $mimeType)){
738
+ $imgType = 'gif';
739
+ imagegif($canvas, $tempfile);
740
+ } else {
741
+ return $this->sanityFail("Could not match mime type after verifying it previously.");
742
+ }
743
+
744
+ if($imgType == 'png' && OPTIPNG_ENABLED && OPTIPNG_PATH && @is_file(OPTIPNG_PATH)){
745
+ $exec = OPTIPNG_PATH;
746
+ $this->debug(3, "optipng'ing $tempfile");
747
+ $presize = filesize($tempfile);
748
+ $out = `$exec -o1 $tempfile`; //you can use up to -o7 but it really slows things down
749
+ clearstatcache();
750
+ $aftersize = filesize($tempfile);
751
+ $sizeDrop = $presize - $aftersize;
752
+ if($sizeDrop > 0){
753
+ $this->debug(1, "optipng reduced size by $sizeDrop");
754
+ } else if($sizeDrop < 0){
755
+ $this->debug(1, "optipng increased size! Difference was: $sizeDrop");
756
+ } else {
757
+ $this->debug(1, "optipng did not change image size.");
758
+ }
759
+ } else if($imgType == 'png' && PNGCRUSH_ENABLED && PNGCRUSH_PATH && @is_file(PNGCRUSH_PATH)){
760
+ $exec = PNGCRUSH_PATH;
761
+ $tempfile2 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
762
+ $this->debug(3, "pngcrush'ing $tempfile to $tempfile2");
763
+ $out = `$exec $tempfile $tempfile2`;
764
+ $todel = "";
765
+ if(is_file($tempfile2)){
766
+ $sizeDrop = filesize($tempfile) - filesize($tempfile2);
767
+ if($sizeDrop > 0){
768
+ $this->debug(1, "pngcrush was succesful and gave a $sizeDrop byte size reduction");
769
+ $todel = $tempfile;
770
+ $tempfile = $tempfile2;
771
+ } else {
772
+ $this->debug(1, "pngcrush did not reduce file size. Difference was $sizeDrop bytes.");
773
+ $todel = $tempfile2;
774
+ }
775
+ } else {
776
+ $this->debug(3, "pngcrush failed with output: $out");
777
+ $todel = $tempfile2;
778
+ }
779
+ @unlink($todel);
780
+ }
781
+
782
+ $this->debug(3, "Rewriting image with security header.");
783
+ $tempfile4 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
784
+ $context = stream_context_create ();
785
+ $fp = fopen($tempfile,'r',0,$context);
786
+ file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>'); //6 extra bytes, first 3 being image type
787
+ file_put_contents($tempfile4, $fp, FILE_APPEND);
788
+ fclose($fp);
789
+ @unlink($tempfile);
790
+ $this->debug(3, "Locking and replacing cache file.");
791
+ $lockFile = $this->cachefile . '.lock';
792
+ $fh = fopen($lockFile, 'w');
793
+ if(! $fh){
794
+ return $this->error("Could not open the lockfile for writing an image.");
795
+ }
796
+ if(flock($fh, LOCK_EX)){
797
+ @unlink($this->cachefile); //rename generally overwrites, but doing this in case of platform specific quirks. File might not exist yet.
798
+ rename($tempfile4, $this->cachefile);
799
+ flock($fh, LOCK_UN);
800
+ fclose($fh);
801
+ @unlink($lockFile);
802
+ } else {
803
+ fclose($fh);
804
+ @unlink($lockFile);
805
+ @unlink($tempfile4);
806
+ return $this->error("Could not get a lock for writing.");
807
+ }
808
+ $this->debug(3, "Done image replace with security header. Cleaning up and running cleanCache()");
809
+ imagedestroy($canvas);
810
+ imagedestroy($image);
811
+ return true;
812
+ }
813
+ protected function calcDocRoot(){
814
+ $docRoot = @$_SERVER['DOCUMENT_ROOT'];
815
+ if (defined('LOCAL_FILE_BASE_DIRECTORY')) {
816
+ $docRoot = LOCAL_FILE_BASE_DIRECTORY;
817
+ }
818
+ if(!isset($docRoot)){
819
+ $this->debug(3, "DOCUMENT_ROOT is not set. This is probably windows. Starting search 1.");
820
+ if(isset($_SERVER['SCRIPT_FILENAME'])){
821
+ $docRoot = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF'])));
822
+ $this->debug(3, "Generated docRoot using SCRIPT_FILENAME and PHP_SELF as: $docRoot");
823
+ }
824
+ }
825
+ if(!isset($docRoot)){
826
+ $this->debug(3, "DOCUMENT_ROOT still is not set. Starting search 2.");
827
+ if(isset($_SERVER['PATH_TRANSLATED'])){
828
+ $docRoot = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF'])));
829
+ $this->debug(3, "Generated docRoot using PATH_TRANSLATED and PHP_SELF as: $docRoot");
830
+ }
831
+ }
832
+ if($docRoot && $_SERVER['DOCUMENT_ROOT'] != '/'){ $docRoot = preg_replace('/\/$/', '', $docRoot); }
833
+ $this->debug(3, "Doc root is: " . $docRoot);
834
+ $this->docRoot = $docRoot;
835
+
836
+ }
837
+ protected function getLocalImagePath($src){
838
+ $src = preg_replace('/^\//', '', $src); //strip off the leading '/'
839
+ if(! $this->docRoot){
840
+ $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.");
841
+ //We don't support serving images outside the current dir if we don't have a doc root for security reasons.
842
+ $file = preg_replace('/^.*?([^\/\\\\]+)$/', '$1', $src); //strip off any path info and just leave the filename.
843
+ if(is_file($file)){
844
+ return realpath($file);
845
+ }
846
+ 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.");
847
+ } //Do not go past this point without docRoot set
848
+
849
+ //Try src under docRoot
850
+ if(file_exists ($this->docRoot . '/' . $src)) {
851
+ $this->debug(3, "Found file as " . $this->docRoot . '/' . $src);
852
+ $real = realpath($this->docRoot . '/' . $src);
853
+ if(stripos($real, $this->docRoot) == 0){
854
+ return $real;
855
+ } else {
856
+ $this->debug(1, "Security block: The file specified occurs outside the document root.");
857
+ //allow search to continue
858
+ }
859
+ }
860
+ //Check absolute paths and then verify the real path is under doc root
861
+ $absolute = realpath('/' . $src);
862
+ if($absolute && file_exists($absolute)){ //realpath does file_exists check, so can probably skip the exists check here
863
+ $this->debug(3, "Found absolute path: $absolute");
864
+ if(! $this->docRoot){ $this->sanityFail("docRoot not set when checking absolute path."); }
865
+ if(stripos($absolute, $this->docRoot) == 0){
866
+ return $absolute;
867
+ } else {
868
+ $this->debug(1, "Security block: The file specified occurs outside the document root.");
869
+ //and continue search
870
+ }
871
+ }
872
+
873
+ $base = $this->docRoot;
874
+
875
+ // account for Windows directory structure
876
+ if (strstr($_SERVER['SCRIPT_FILENAME'],':')) {
877
+ $sub_directories = explode('\\', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME']));
878
+ } else {
879
+ $sub_directories = explode('/', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME']));
880
+ }
881
+
882
+ foreach ($sub_directories as $sub){
883
+ $base .= $sub . '/';
884
+ $this->debug(3, "Trying file as: " . $base . $src);
885
+ if(file_exists($base . $src)){
886
+ $this->debug(3, "Found file as: " . $base . $src);
887
+ $real = realpath($base . $src);
888
+ if(stripos($real, $this->docRoot) == 0){
889
+ return $real;
890
+ } else {
891
+ $this->debug(1, "Security block: The file specified occurs outside the document root.");
892
+ //And continue search
893
+ }
894
+ }
895
+ }
896
+ return false;
897
+ }
898
+ protected function toDelete($name){
899
+ $this->debug(3, "Scheduling file $name to delete on destruct.");
900
+ $this->toDeletes[] = $name;
901
+ }
902
+ protected function serveWebshot(){
903
+ $this->debug(3, "Starting serveWebshot");
904
+ $instr = "Please follow the instructions at http://code.google.com/p/timthumb/ to set your server up for taking website screenshots.";
905
+ if(! is_file(WEBSHOT_CUTYCAPT)){
906
+ return $this->error("CutyCapt is not installed. $instr");
907
+ }
908
+ if(! is_file(WEBSHOT_XVFB)){
909
+ return $this->Error("Xvfb is not installed. $instr");
910
+ }
911
+ $cuty = WEBSHOT_CUTYCAPT;
912
+ $xv = WEBSHOT_XVFB;
913
+ $screenX = WEBSHOT_SCREEN_X;
914
+ $screenY = WEBSHOT_SCREEN_Y;
915
+ $colDepth = WEBSHOT_COLOR_DEPTH;
916
+ $format = WEBSHOT_IMAGE_FORMAT;
917
+ $timeout = WEBSHOT_TIMEOUT * 1000;
918
+ $ua = WEBSHOT_USER_AGENT;
919
+ $jsOn = WEBSHOT_JAVASCRIPT_ON ? 'on' : 'off';
920
+ $javaOn = WEBSHOT_JAVA_ON ? 'on' : 'off';
921
+ $pluginsOn = WEBSHOT_PLUGINS_ON ? 'on' : 'off';
922
+ $proxy = WEBSHOT_PROXY ? ' --http-proxy=' . WEBSHOT_PROXY : '';
923
+ $tempfile = tempnam($this->cacheDirectory, 'timthumb_webshot');
924
+ $url = $this->src;
925
+ if(! preg_match('/^https?:\/\/[a-zA-Z0-9\.\-]+/i', $url)){
926
+ return $this->error("Invalid URL supplied.");
927
+ }
928
+ $url = preg_replace('/[^A-Za-z0-9\-\.\_\~:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=]+/', '', $url); //RFC 3986
929
+ //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
930
+ // which AFAIKT can't be used for shell injection.
931
+ if(WEBSHOT_XVFB_RUNNING){
932
+ putenv('DISPLAY=:100.0');
933
+ $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";
934
+ } else {
935
+ $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";
936
+ }
937
+ $this->debug(3, "Executing command: $command");
938
+ $out = `$command`;
939
+ $this->debug(3, "Received output: $out");
940
+ if(! is_file($tempfile)){
941
+ $this->set404();
942
+ return $this->error("The command to create a thumbnail failed.");
943
+ }
944
+ $this->cropTop = true;
945
+ if($this->processImageAndWriteToCache($tempfile)){
946
+ $this->debug(3, "Image processed succesfully. Serving from cache");
947
+ return $this->serveCacheFile();
948
+ } else {
949
+ return false;
950
+ }
951
+ }
952
+ protected function serveExternalImage(){
953
+ if(! preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+/i', $this->src)){
954
+ $this->error("Invalid URL supplied.");
955
+ return false;
956
+ }
957
+ $tempfile = tempnam($this->cacheDirectory, 'timthumb');
958
+ $this->debug(3, "Fetching external image into temporary file $tempfile");
959
+ $this->toDelete($tempfile);
960
+ #fetch file here
961
+ if(! $this->getURL($this->src, $tempfile)){
962
+ @unlink($this->cachefile);
963
+ touch($this->cachefile);
964
+ $this->debug(3, "Error fetching URL: " . $this->lastURLError);
965
+ $this->error("Error reading the URL you specified from remote host." . $this->lastURLError);
966
+ return false;
967
+ }
968
+
969
+ $mimeType = $this->getMimeType($tempfile);
970
+ if(! preg_match("/^image\/(?:jpg|jpeg|gif|png)$/i", $mimeType)){
971
+ $this->debug(3, "Remote file has invalid mime type: $mimeType");
972
+ @unlink($this->cachefile);
973
+ touch($this->cachefile);
974
+ $this->error("The remote file is not a valid image.");
975
+ return false;
976
+ }
977
+ if($this->processImageAndWriteToCache($tempfile)){
978
+ $this->debug(3, "Image processed succesfully. Serving from cache");
979
+ return $this->serveCacheFile();
980
+ } else {
981
+ return false;
982
+ }
983
+ }
984
+ public static function curlWrite($h, $d){
985
+ fwrite(self::$curlFH, $d);
986
+ self::$curlDataWritten += strlen($d);
987
+ if(self::$curlDataWritten > MAX_FILE_SIZE){
988
+ return 0;
989
+ } else {
990
+ return strlen($d);
991
+ }
992
+ }
993
+ protected function serveCacheFile(){
994
+ $this->debug(3, "Serving {$this->cachefile}");
995
+ if(! is_file($this->cachefile)){
996
+ $this->error("serveCacheFile called in timthumb but we couldn't find the cached file.");
997
+ return false;
998
+ }
999
+ $fp = fopen($this->cachefile, 'rb');
1000
+ if(! $fp){ return $this->error("Could not open cachefile."); }
1001
+ fseek($fp, strlen($this->filePrependSecurityBlock), SEEK_SET);
1002
+ $imgType = fread($fp, 3);
1003
+ fseek($fp, 3, SEEK_CUR);
1004
+ if(ftell($fp) != strlen($this->filePrependSecurityBlock) + 6){
1005
+ @unlink($this->cachefile);
1006
+ return $this->error("The cached image file seems to be corrupt.");
1007
+ }
1008
+ $imageDataSize = filesize($this->cachefile) - (strlen($this->filePrependSecurityBlock) + 6);
1009
+ $this->sendImageHeaders($imgType, $imageDataSize);
1010
+ $bytesSent = @fpassthru($fp);
1011
+ fclose($fp);
1012
+ if($bytesSent > 0){
1013
+ return true;
1014
+ }
1015
+ $content = file_get_contents ($this->cachefile);
1016
+ if ($content != FALSE) {
1017
+ $content = substr($content, strlen($this->filePrependSecurityBlock) + 6);
1018
+ echo $content;
1019
+ $this->debug(3, "Served using file_get_contents and echo");
1020
+ return true;
1021
+ } else {
1022
+ $this->error("Cache file could not be loaded.");
1023
+ return false;
1024
+ }
1025
+ }
1026
+ protected function sendImageHeaders($mimeType, $dataSize){
1027
+ if(! preg_match('/^image\//i', $mimeType)){
1028
+ $mimeType = 'image/' . $mimeType;
1029
+ }
1030
+ if(strtolower($mimeType) == 'image/jpg'){
1031
+ $mimeType = 'image/jpeg';
1032
+ }
1033
+ $gmdate_expires = gmdate ('D, d M Y H:i:s', strtotime ('now +10 days')) . ' GMT';
1034
+ $gmdate_modified = gmdate ('D, d M Y H:i:s') . ' GMT';
1035
+ // send content headers then display image
1036
+ header ('Content-Type: ' . $mimeType);
1037
+ header ('Accept-Ranges: none'); //Changed this because we don't accept range requests
1038
+ header ('Last-Modified: ' . $gmdate_modified);
1039
+ header ('Content-Length: ' . $dataSize);
1040
+ if(BROWSER_CACHE_DISABLE){
1041
+ $this->debug(3, "Browser cache is disabled so setting non-caching headers.");
1042
+ header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
1043
+ header("Pragma: no-cache");
1044
+ header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
1045
+ } else {
1046
+ $this->debug(3, "Browser caching is enabled");
1047
+ header('Cache-Control: max-age=' . BROWSER_CACHE_MAX_AGE . ', must-revalidate');
1048
+ header('Expires: ' . $gmdate_expires);
1049
+ }
1050
+ return true;
1051
+ }
1052
+ protected function securityChecks(){
1053
+ }
1054
+ protected function param($property, $default = ''){
1055
+ if (isset ($_GET[$property])) {
1056
+ return $_GET[$property];
1057
+ } else {
1058
+ return $default;
1059
+ }
1060
+ }
1061
+ protected function openImage($mimeType, $src){
1062
+ switch ($mimeType) {
1063
+ case 'image/jpeg':
1064
+ $image = imagecreatefromjpeg ($src);
1065
+ break;
1066
+
1067
+ case 'image/png':
1068
+ $image = imagecreatefrompng ($src);
1069
+ break;
1070
+
1071
+ case 'image/gif':
1072
+ $image = imagecreatefromgif ($src);
1073
+ break;
1074
+
1075
+ default:
1076
+ $this->error("Unrecognised mimeType");
1077
+ }
1078
+
1079
+ return $image;
1080
+ }
1081
+ protected function getIP(){
1082
+ $rem = @$_SERVER["REMOTE_ADDR"];
1083
+ $ff = @$_SERVER["HTTP_X_FORWARDED_FOR"];
1084
+ $ci = @$_SERVER["HTTP_CLIENT_IP"];
1085
+ if(preg_match('/^(?:192\.168|172\.16|10\.|127\.)/', $rem)){
1086
+ if($ff){ return $ff; }
1087
+ if($ci){ return $ci; }
1088
+ return $rem;
1089
+ } else {
1090
+ if($rem){ return $rem; }
1091
+ if($ff){ return $ff; }
1092
+ if($ci){ return $ci; }
1093
+ return "UNKNOWN";
1094
+ }
1095
+ }
1096
+ protected function debug($level, $msg){
1097
+ if(DEBUG_ON && $level <= DEBUG_LEVEL){
1098
+ $execTime = sprintf('%.6f', microtime(true) - $this->startTime);
1099
+ $tick = sprintf('%.6f', 0);
1100
+ if($this->lastBenchTime > 0){
1101
+ $tick = sprintf('%.6f', microtime(true) - $this->lastBenchTime);
1102
+ }
1103
+ $this->lastBenchTime = microtime(true);
1104
+ error_log("TimThumb Debug line " . __LINE__ . " [$execTime : $tick]: $msg");
1105
+ }
1106
+ }
1107
+ protected function sanityFail($msg){
1108
+ 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");
1109
+ }
1110
+ protected function getMimeType($file){
1111
+ $info = getimagesize($file);
1112
+ if(is_array($info) && $info['mime']){
1113
+ return $info['mime'];
1114
+ }
1115
+ return '';
1116
+ }
1117
+ protected function setMemoryLimit(){
1118
+ $inimem = ini_get('memory_limit');
1119
+ $inibytes = timthumb::returnBytes($inimem);
1120
+ $ourbytes = timthumb::returnBytes(MEMORY_LIMIT);
1121
+ if($inibytes < $ourbytes){
1122
+ ini_set ('memory_limit', MEMORY_LIMIT);
1123
+ $this->debug(3, "Increased memory from $inimem to " . MEMORY_LIMIT);
1124
+ } else {
1125
+ $this->debug(3, "Not adjusting memory size because the current setting is " . $inimem . " and our size of " . MEMORY_LIMIT . " is smaller.");
1126
+ }
1127
+ }
1128
+ protected static function returnBytes($size_str){
1129
+ switch (substr ($size_str, -1))
1130
+ {
1131
+ case 'M': case 'm': return (int)$size_str * 1048576;
1132
+ case 'K': case 'k': return (int)$size_str * 1024;
1133
+ case 'G': case 'g': return (int)$size_str * 1073741824;
1134
+ default: return $size_str;
1135
+ }
1136
+ }
1137
+ protected function getURL($url, $tempfile){
1138
+ $this->lastURLError = false;
1139
+ $url = preg_replace('/ /', '%20', $url);
1140
+ if(function_exists('curl_init')){
1141
+ $this->debug(3, "Curl is installed so using it to fetch URL.");
1142
+ self::$curlFH = fopen($tempfile, 'w');
1143
+ if(! self::$curlFH){
1144
+ $this->error("Could not open $tempfile for writing.");
1145
+ return false;
1146
+ }
1147
+ self::$curlDataWritten = 0;
1148
+ $this->debug(3, "Fetching url with curl: $url");
1149
+ $curl = curl_init($url);
1150
+ curl_setopt ($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
1151
+ 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");
1152
+ curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
1153
+ curl_setopt ($curl, CURLOPT_HEADER, 0);
1154
+ curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
1155
+ curl_setopt ($curl, CURLOPT_WRITEFUNCTION, 'timthumb::curlWrite');
1156
+ @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
1157
+ @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
1158
+
1159
+ $curlResult = curl_exec($curl);
1160
+ fclose(self::$curlFH);
1161
+ $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
1162
+ if($httpStatus == 404){
1163
+ $this->set404();
1164
+ }
1165
+ if($curlResult){
1166
+ curl_close($curl);
1167
+ return true;
1168
+ } else {
1169
+ $this->lastURLError = curl_error($curl);
1170
+ curl_close($curl);
1171
+ return false;
1172
+ }
1173
+ } else {
1174
+ $img = @file_get_contents ($url);
1175
+ if($img === false){
1176
+ $err = error_get_last();
1177
+ if(is_array($err) && $err['message']){
1178
+ $this->lastURLError = $err['message'];
1179
+ } else {
1180
+ $this->lastURLError = $err;
1181
+ }
1182
+ if(preg_match('/404/', $this->lastURLError)){
1183
+ $this->set404();
1184
+ }
1185
+
1186
+ return false;
1187
+ }
1188
+ if(! file_put_contents($tempfile, $img)){
1189
+ $this->error("Could not write to $tempfile.");
1190
+ return false;
1191
+ }
1192
+ return true;
1193
+ }
1194
+
1195
+ }
1196
+ protected function serveImg($file){
1197
+ $s = getimagesize($file);
1198
+ if(! ($s && $s['mime'])){
1199
+ return false;
1200
+ }
1201
+ header ('Content-Type: ' . $s['mime']);
1202
+ header ('Content-Length: ' . filesize($file) );
1203
+ header ('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
1204
+ header ("Pragma: no-cache");
1205
+ $bytes = @readfile($file);
1206
+ if($bytes > 0){
1207
+ return true;
1208
+ }
1209
+ $content = @file_get_contents ($file);
1210
+ if ($content != FALSE){
1211
+ echo $content;
1212
+ return true;
1213
+ }
1214
+ return false;
1215
+
1216
+ }
1217
+ protected function set404(){
1218
+ $this->is404 = true;
1219
+ }
1220
+ protected function is404(){
1221
+ return $this->is404;
1222
+ }
1223
+ }
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: gn_themes
3
  Donate link: http://gndev.info/donate/
4
  Tags: shortcode, shortcodes, short code, shortcodes, tab, tabs, button, buttons, jquery, box, boxes, toggle, spoiler, column, columns, services, service, pullquote, list, lists, frame, images, image, links, fancy, fancy link, fancy links, fancy buttons, jquery tabs, accordeon, slider, nivo, nivo slider, plugin, admin, photoshop, gallery, bloginfo, list pages, sub pages, navigation, siblings pages, children pages, permalink, permalinks, feed, document, member, members, documents, jcarousel, rss
5
  Requires at least: 3.0
6
- Tested up to: 3.3
7
- Stable tag: 3.6.1
8
 
9
  Provides support for multiple useful shortcodes
10
 
@@ -21,14 +21,10 @@ With this plugin you can easily create buttons, boxes, different sliders and muc
21
  * International
22
 
23
  = New in this version =
24
- * New shortcode [accordion] for muliple spoilers
25
- * Improved spoiler shortcode (check settings page)
26
- * Multiple tabs bugfix
27
- * Authors can also use shortcode generator
28
- * Descriptions for [custom_gallery]
29
- * Custom options for jwPlayer
30
- * Fixed size option for sliders and gallery
31
- * Custom styling for [custom_gallery]
32
 
33
  = Got a bug? =
34
  * [Support forum](http://wordpress.org/tags/shortcodes-ultimate?forum_id=10)
3
  Donate link: http://gndev.info/donate/
4
  Tags: shortcode, shortcodes, short code, shortcodes, tab, tabs, button, buttons, jquery, box, boxes, toggle, spoiler, column, columns, services, service, pullquote, list, lists, frame, images, image, links, fancy, fancy link, fancy links, fancy buttons, jquery tabs, accordeon, slider, nivo, nivo slider, plugin, admin, photoshop, gallery, bloginfo, list pages, sub pages, navigation, siblings pages, children pages, permalink, permalinks, feed, document, member, members, documents, jcarousel, rss
5
  Requires at least: 3.0
6
+ Tested up to: 4.0
7
+ Stable tag: 3.7.0
8
 
9
  Provides support for multiple useful shortcodes
10
 
21
  * International
22
 
23
  = New in this version =
24
+ * Complete support for nested shortcodes. Check the FAQ page.
25
+ * New shortcode [label]
26
+ * New style for buttons [button style="5"]
27
+ * Fixed images ordering for [custom_gallery], [jcarousel] and [nivo_slider]
 
 
 
 
28
 
29
  = Got a bug? =
30
  * [Support forum](http://wordpress.org/tags/shortcodes-ultimate?forum_id=10)
shortcodes-ultimate.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Shortcodes Ultimate
4
  Plugin URI: http://gndev.info/shortcodes-ultimate/
5
- Version: 3.6.1
6
  Author: Vladimir Anokhin
7
  Author URI: http://gndev.info/
8
  Description: Provides support for many easy to use shortcodes
@@ -187,9 +187,9 @@
187
  /*
188
  * Custom shortcode function for nested shortcodes support
189
  */
190
- function su_do_shortcode( $content ) {
191
- if ( strpos( $content, '[=' ) !== false ) {
192
- $content = preg_replace( '@(\[=*)=(s|/)@', "$1$2", $content );
193
  }
194
  return do_shortcode( $content );
195
  }
2
  /*
3
  Plugin Name: Shortcodes Ultimate
4
  Plugin URI: http://gndev.info/shortcodes-ultimate/
5
+ Version: 3.7.0
6
  Author: Vladimir Anokhin
7
  Author URI: http://gndev.info/
8
  Description: Provides support for many easy to use shortcodes
187
  /*
188
  * Custom shortcode function for nested shortcodes support
189
  */
190
+ function su_do_shortcode( $content, $modifier ) {
191
+ if ( strpos( $content, '[_' ) !== false ) {
192
+ $content = preg_replace( '@(\[_*)_(' . $modifier . '|/)@', "$1$2", $content );
193
  }
194
  return do_shortcode( $content );
195
  }
todo.txt CHANGED
@@ -1,4 +1 @@
1
- Shortcode [twitter]/[tweet]
2
- Shortcode [label]
3
- Shortcode [pricing] + [plan] (pricing tables)
4
- Nested shortcodes
1
+ Shortcode [pricing] + [plan] (pricing tables)