FameTheme Demo Importer - Version 1.0.7

Version Description

Download this release

Release Info

Developer FameThemes
Plugin Icon wp plugin FameTheme Demo Importer
Version 1.0.7
Comparing to
See all releases

Code changes from version 1.0.3 to 1.0.7

assets/css/importer.css DELETED
@@ -1,55 +0,0 @@
1
- .ft-demo-import-now.button.updating-message:before {
2
- margin: 13px 5px 0 -2px;
3
- }
4
-
5
- .ft-import-box {
6
- background: #fff none repeat scroll 0 0;
7
- box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
8
- font-size: 14px;
9
- line-height: 2.1em;
10
- margin-bottom: 25px;
11
- overflow: auto;
12
- padding: 0px 12px 0px 12px;
13
- position: relative;
14
- max-width: 1050px;
15
- }
16
- .ft-import-box.ft-import-welcome {
17
- padding: 0px 20px 10px 25px;
18
- }
19
- .ft-import-box h3 {
20
- font-size: 22px;
21
- font-weight: 500;
22
- }
23
- .ft-import-box li {
24
- margin: 0px;
25
- padding-left: 15px;
26
- list-style: inside;
27
- }
28
- .ft-export-config {
29
- left: 20px;
30
- position: relative;
31
- top: 10px;
32
- }
33
- .ft-import-action {
34
- margin-bottom: 25px;
35
- }
36
- .ft-import-theme {
37
- border-left: 4px solid #0085ba;
38
- }
39
- .ft-import-success {
40
- border-left-color: #46b450;
41
- }
42
- .ft-import-error {
43
- border-left-color: #dc3232;
44
- }
45
- .recommend-plugins {
46
- background: #fff none repeat scroll 0 0;
47
- box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
48
- font-size: 14px;
49
- line-height: 2.1em;
50
- margin-bottom: 25px;
51
- max-width: 1050px;
52
- overflow: auto;
53
- padding: 0px 25px 10px;
54
- position: relative;
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/images/sprite.svg ADDED
@@ -0,0 +1,648 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <svg id="sprite" xmlns="http://www.w3.org/2000/svg" version="1.0" class="hidden">
2
+
3
+ <symbol id="icon-merlin" width="80px" height="122px" viewBox="0 0 80 122" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
4
+ <defs></defs>
5
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
6
+ <path d="M17,60.7982835 L37.9947017,2.5892267 C39.110827,-0.863075567 40.9197197,-0.863075567 42.0366469,2.5892267 L62.9521959,60.7982835 L17,60.7982835 Z" id="Fill-5" fill="#0073AA"></path>
7
+ <path d="M43.5015868,54.4568084 C43.3083496,54.4568084 43.1054906,54.3976555 42.8970189,54.2810642 L40.588596,52.9839861 L38.280173,54.2810642 C37.7862555,54.5579685 37.2931398,54.4928145 37.0125048,54.137897 C36.8769983,53.9664392 36.7342753,53.654386 36.8152586,53.1434419 L37.2562563,50.3949738 L35.389633,48.448928 C35.0536729,48.0991542 34.9269862,47.6833691 35.0408438,47.3087338 C35.1538996,46.9366705 35.4850489,46.6794838 35.9484975,46.6074716 L38.5295372,46.2071177 L39.682546,43.706406 C39.8902158,43.2571867 40.2205633,43 40.588596,43 C40.9558268,43 41.2861743,43.2571867 41.4946459,43.706406 L42.6484565,46.2071177 L45.2278926,46.6074716 C45.6913412,46.6794838 46.0232923,46.9366705 46.1371499,47.3104484 C46.2510075,47.6850837 46.123519,48.0991542 45.7875589,48.448928 L43.9201337,50.3949738 L44.3611315,53.1434419 C44.4429166,53.654386 44.2993918,53.9664392 44.1646871,54.137897 C44.0019188,54.3436463 43.7669872,54.4568084 43.5015868,54.4568084" id="Fill-6" fill="#FFEB72"></path>
8
+ <path d="M43.0110526,23.4481806 C42.8514916,23.4481806 42.6847142,23.3993151 42.5123242,23.3032988 L40.6088174,22.2334023 L38.7053106,23.3032988 C38.2971872,23.5321949 37.8914692,23.4781857 37.6589431,23.1849929 C37.5482928,23.0435403 37.4296243,22.7863536 37.4961749,22.3645675 L37.8601985,20.098753 L36.3215171,18.493051 C36.0440894,18.2050019 35.9398536,17.8629436 36.0336659,17.5534624 C36.1266763,17.2465529 36.3992931,17.0348026 36.7817585,16.9747924 L38.910575,16.6447362 L39.8623284,14.5829564 C40.0331148,14.2117504 40.3057316,14 40.6088174,14 C40.9119031,14 41.18452,14.2117504 41.3561082,14.5829564 L42.3070598,16.6447362 L44.4358763,16.9747924 C44.8175399,17.0348026 45.0917603,17.2465529 45.1847708,17.5543196 C45.278583,17.8629436 45.1735454,18.2050019 44.8969195,18.493051 L43.3566345,20.098753 L43.7198563,22.3645675 C43.7872087,22.7863536 43.669342,23.0435403 43.5586917,23.1849929 C43.4239869,23.3547361 43.2307497,23.4481806 43.0110526,23.4481806" id="Fill-7" fill="#FFEB72"></path>
9
+ <path d="M26.3759131,38.0563447 L28.4710535,39.2342596 C28.6594798,39.3405634 28.843897,39.3937153 29.0186925,39.3937153 C29.2600386,39.3937153 29.4733211,39.2908406 29.6216568,39.1039517 C29.7427307,38.9479251 29.8742282,38.665877 29.7988577,38.2012265 L29.3995542,35.7082305 L31.0929857,33.9430727 C31.3976751,33.6258758 31.5131363,33.249526 31.4113059,32.9100396 C31.3070701,32.5705532 31.0063898,32.3382279 30.5854374,32.2722167 L28.2449419,31.9104408 L27.8247913,31 L25,38.8304765 L26.3759131,38.0563447" id="Fill-9" fill="#FFEB72"></path>
10
+ <path d="M51.0472167,35.7536668 L50.6471115,38.2466628 C50.5733446,38.7113134 50.7032385,38.9933614 50.8251142,39.149388 C51.0808929,39.4717286 51.5275034,39.5317388 51.9765193,39.2796959 L54.0700561,38.101781 L55.4499782,38.8776274 L52.6428269,31 L52.2010273,31.9558771 L49.8605318,32.317653 C49.4403812,32.3836642 49.1397009,32.6159895 49.0370686,32.9546186 C48.9336346,33.294105 49.0490959,33.6713121 49.3529834,33.988509 L51.0472167,35.7536668" id="Fill-10" fill="#FFEB72"></path>
11
+ <path d="M69.1847569,80.2753378 L62.307597,59 L32.9042703,59.0017146 L32.6244371,59.0162885 C32.504165,59.0240041 32.3846947,59.032577 32.2764498,59.0334343 C32.2331518,59.0317197 32.1922593,59.0240041 32.1160869,59.0137166 L20.0239277,59 L19.6751386,59.1680286 C19.3784673,59.3120531 19.0344891,59.4732235 18.7265924,59.5563805 C18.4948682,59.6189626 18.2430985,59.6875457 17.9760944,59.7801329 L17.9680763,59.7595579 C17.8967148,59.7904203 17.8245515,59.8135671 17.705883,59.8555743 L16.8687891,60.1496244 L10.5216282,80.0215803 C9.01822671,84.7289535 5.88874608,94.5226215 10.156001,104.349724 C14.9283988,115.342739 24.2815609,121.647241 35.8156574,121.647241 L35.8156574,121.597518 C36.6038407,121.669531 37.8674998,121.671245 39.9714602,121.675532 L44.1256593,121.679818 C55.2267762,121.6541 64.2215273,115.605069 69.4549683,104.644631 C73.9932364,95.1424413 70.7458891,85.1001595 69.1847569,80.2753378" id="Fill-22" fill="#DBDEE4"></path>
12
+ <path d="M16,75.6536386 C16,90.0055115 21.4563453,96.7892383 40.3029869,96.7892383 C59.3484784,96.7892383 64.6075774,90.0055115 64.6075774,75.6536386 C64.6075774,70.2167126 63.043238,65.1724249 60.3739987,61 L20.2327769,61 C17.5635376,65.1724249 16,70.2167126 16,75.6536386 Z" id="Fill-23" fill="#FBDFD3"></path>
13
+ <path d="M56.4181479,97.838813 C56.4181479,105.496974 50.6122117,111.703746 43.4504077,111.703746 L36.9661366,111.703746 C29.8059363,111.703746 24,105.496974 24,97.838813 L24,96.8649328 C24,89.2076287 29.8059363,83 36.9661366,83 L43.4504077,83 C50.6122117,83 56.4181479,89.2076287 56.4181479,96.8649328 L56.4181479,97.838813" id="Fill-24" fill="#FFFFFF"></path>
14
+ <path d="M76.8875625,63.6692513 L2.65079758,63.6692513 C1.18588313,63.6692513 0,62.4004638 0,60.834197 C0,59.2687875 1.18588313,58 2.65079758,58 L76.8875625,58 C78.352477,58 79.5391619,59.2687875 79.5391619,60.834197 C79.5391619,62.4004638 78.352477,63.6692513 76.8875625,63.6692513" id="Fill-25" fill="#0073AA"></path>
15
+ <path d="M44.9432973,80.3238188 C44.9432973,83.8455615 42.7166594,84.811726 39.9712477,84.811726 C37.2266379,84.811726 35,83.8455615 35,80.3238188 C35,76.8012188 37.2266379,73 39.9712477,73 C42.7166594,73 44.9432973,76.8012188 44.9432973,80.3238188" id="Fill-26" fill="#F0CCBC"></path>
16
+ <path d="M34.7605967,67.834197 C34.7605967,69.4004638 33.5739118,70.6692513 32.1097991,70.6692513 L22.6515994,70.6692513 C21.1858831,70.6692513 20,69.4004638 20,67.834197 C20,66.2687875 21.1858831,65 22.6515994,65 L32.1097991,65 C33.5739118,65 34.7605967,66.2687875 34.7605967,67.834197" id="Fill-27" fill="#FFFFFF"></path>
17
+ <path d="M59.7613985,67.834197 C59.7613985,69.4004638 58.5747136,70.6692513 57.1097991,70.6692513 L47.6515994,70.6692513 C46.1866849,70.6692513 45,69.4004638 45,67.834197 C45,66.2687875 46.1866849,65 47.6515994,65 L57.1097991,65 C58.5747136,65 59.7613985,66.2687875 59.7613985,67.834197" id="Fill-28" fill="#FFFFFF"></path>
18
+ <path d="M29.9087847,77.7245189 C29.4132636,77.7245189 28.9377878,77.4270397 28.7108744,76.9160955 C28.4134014,76.2491248 27.8040226,75.8350543 27.1168679,75.8350543 C26.4297131,75.8350543 25.8195326,76.2491248 25.5228613,76.9160955 C25.2085502,77.6242161 24.4179614,77.9225527 23.7572666,77.5873527 C23.0957699,77.2512955 22.8143331,76.4060087 23.1286443,75.6996026 C23.8574934,74.059609 25.4226346,73 27.1168679,73 C28.8111012,73 30.3770442,74.059609 31.1050915,75.6996026 C31.4186008,76.4060087 31.1379658,77.2512955 30.477271,77.5873527 C30.2928537,77.6807972 30.0996165,77.7245189 29.9087847,77.7245189" id="Fill-29" fill="#220B68"></path>
19
+ <path d="M54.9087847,77.7245189 C54.4140654,77.7245189 53.9377878,77.4270397 53.7116762,76.9160955 C53.4142032,76.2491248 52.8040226,75.8350543 52.1168679,75.8350543 C51.4297131,75.8350543 50.8195326,76.2491248 50.5228613,76.9160955 C50.209352,77.6242161 49.4187633,77.9225527 48.7572666,77.5873527 C48.0957699,77.2512955 47.8143331,76.4060087 48.1286443,75.6996026 C48.8574934,74.059609 50.4226346,73 52.1168679,73 C53.8111012,73 55.3770442,74.059609 56.1050915,75.6996026 C56.4194026,76.4060087 56.1379658,77.2512955 55.477271,77.5873527 C55.2928537,77.6807972 55.0996165,77.7245189 54.9087847,77.7245189" id="Fill-40" fill="#220B68"></path>
20
+ <path d="M46.373596,88 C46.373596,91.0665223 43.6041299,93.5500881 40.186798,93.5500881 C36.7694661,93.5500881 34,91.0665223 34,88 L46.373596,88" id="Fill-41" fill="#220B68"></path>
21
+ </g>
22
+
23
+ </symbol>
24
+
25
+ <symbol id="icon-welcome" width="166px" height="105px" viewBox="0 0 166 105" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
26
+ <defs></defs>
27
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
28
+ <g id="Getting-Started">
29
+ <g id="getting-started">
30
+ <polygon id="Fill-310" fill="#FFFFFF" points="130.588 73.184 139.861 73.184 139.861 67.506 130.588 67.506"></polygon>
31
+ <path d="M142.664,103.301 L127.943,103.301 L127.943,72.114 C127.943,71.346 128.566,70.723 129.335,70.723 L141.273,70.723 C142.041,70.723 142.664,71.346 142.664,72.114 L142.664,103.301" id="Fill-312" fill="#FFFFFF"></path>
32
+ <path d="M149.481,94.952 C149.461,95.921 148.66,96.69 147.692,96.671 C146.723,96.652 145.954,95.851 145.973,94.882 C145.992,93.914 146.793,93.144 147.762,93.163 C148.73,93.183 149.5,93.984 149.481,94.952" id="Fill-322" fill="#FFFFFF"></path>
33
+ <path d="M136.324881,101.023 C135.858427,101.023 135.476088,100.603 135.436155,100.046 C135.341845,98.695 134.442073,97.641 133.295905,97.537 C132.828602,97.494 132.476,97.051 132.476,96.506 L132.476,93.7 L133.750464,93.7 L133.750464,96.094 C135.239888,96.403 136.39965,97.767 136.662189,99.523 L140.749,99.523 L140.749,101.023 L136.324881,101.023 Z" id="Fill-326" fill="#0073AA"></path>
34
+ <path d="M151.667,103.665 L151.667,100.387 C151.667,100.097 151.902,99.862 152.192,99.862 L164.85,99.862 C165.14,99.862 165.375,100.097 165.375,100.387 L165.375,103.665 L151.667,103.665" id="Fill-327" fill="#FFFFFF"></path>
35
+ <path d="M155.125,103.665 L153.625,103.665 L153.625,100.612 L141.417,100.612 L141.417,103.665 L139.917,103.665 L139.917,100.387 C139.917,99.684 140.489,99.112 141.192,99.112 L153.85,99.112 C154.553,99.112 155.125,99.684 155.125,100.387 L155.125,103.665" id="Fill-328" fill="#0073AA"></path>
36
+ <rect id="Rectangle" fill="#E7F7FF" x="141" y="95" width="14" height="4"></rect>
37
+ <path d="M142.159,98.554 L154.367,98.554 L154.367,95.725 L142.159,95.725 L142.159,98.554 Z M154.728,100.054 L141.798,100.054 C141.17,100.054 140.659,99.542 140.659,98.914 L140.659,95.363 C140.659,94.735 141.17,94.225 141.798,94.225 L154.728,94.225 C155.356,94.225 155.867,94.735 155.867,95.363 L155.867,98.914 C155.867,99.542 155.356,100.054 154.728,100.054 L154.728,100.054 Z" id="Fill-330" fill="#0073AA"></path>
38
+ <path d="M141.55,93.666 L153.758,93.666 L153.758,90.838 L141.55,90.838 L141.55,93.666 Z M154.119,95.166 L141.188,95.166 C140.56,95.166 140.05,94.655 140.05,94.027 L140.05,90.477 C140.05,89.849 140.56,89.338 141.188,89.338 L154.119,89.338 C154.747,89.338 155.258,89.849 155.258,90.477 L155.258,94.027 C155.258,94.655 154.747,95.166 154.119,95.166 L154.119,95.166 Z" id="Fill-332" fill="#0073AA"></path>
39
+ <path d="M101.438,7.16 L97.164,7.16 C96.75,7.16 96.414,6.824 96.414,6.41 C96.414,5.996 96.75,5.66 97.164,5.66 L101.438,5.66 C101.852,5.66 102.188,5.996 102.188,6.41 C102.188,6.824 101.852,7.16 101.438,7.16" id="Fill-340" fill="#0073AA"></path>
40
+ <path d="M99.301,9.297 C98.887,9.297 98.551,8.961 98.551,8.547 L98.551,4.273 C98.551,3.859 98.887,3.523 99.301,3.523 C99.715,3.523 100.051,3.859 100.051,4.273 L100.051,8.547 C100.051,8.961 99.715,9.297 99.301,9.297" id="Fill-341" fill="#0073AA"></path>
41
+ <path d="M42.733,24.023 L38.459,24.023 C38.045,24.023 37.709,23.688 37.709,23.273 C37.709,22.859 38.045,22.523 38.459,22.523 L42.733,22.523 C43.147,22.523 43.483,22.859 43.483,23.273 C43.483,23.688 43.147,24.023 42.733,24.023" id="Fill-342" fill="#0073AA"></path>
42
+ <path d="M40.596,26.16 C40.182,26.16 39.846,25.824 39.846,25.41 L39.846,21.136 C39.846,20.722 40.182,20.386 40.596,20.386 C41.01,20.386 41.346,20.722 41.346,21.136 L41.346,25.41 C41.346,25.824 41.01,26.16 40.596,26.16" id="Fill-343" fill="#0073AA"></path>
43
+ <path d="M152.15,52.938 C151.958,52.938 151.767,52.864 151.62,52.718 C151.327,52.425 151.327,51.95 151.62,51.657 L154.643,48.635 C154.935,48.342 155.41,48.342 155.703,48.635 C155.996,48.928 155.996,49.402 155.703,49.695 L152.681,52.718 C152.534,52.864 152.343,52.938 152.15,52.938" id="Fill-344" fill="#0073AA"></path>
44
+ <path d="M155.173,52.938 C154.98,52.938 154.789,52.864 154.643,52.718 L151.62,49.695 C151.327,49.402 151.327,48.928 151.62,48.635 C151.913,48.342 152.388,48.342 152.681,48.635 L155.703,51.657 C155.996,51.95 155.996,52.425 155.703,52.718 C155.557,52.864 155.365,52.938 155.173,52.938" id="Fill-345" fill="#0073AA"></path>
45
+ <path d="M5.332,64.237 C5.14,64.237 4.948,64.164 4.802,64.018 C4.509,63.725 4.509,63.25 4.802,62.957 L7.824,59.935 C8.117,59.642 8.592,59.642 8.885,59.935 C9.178,60.228 9.178,60.702 8.885,60.995 L5.862,64.018 C5.716,64.164 5.524,64.237 5.332,64.237" id="Fill-346" fill="#0073AA"></path>
46
+ <path d="M8.354,64.237 C8.162,64.237 7.971,64.164 7.824,64.018 L4.802,60.995 C4.509,60.702 4.509,60.228 4.802,59.935 C5.095,59.642 5.569,59.642 5.862,59.935 L8.885,62.957 C9.178,63.25 9.178,63.725 8.885,64.018 C8.738,64.164 8.547,64.237 8.354,64.237" id="Fill-347" fill="#0073AA"></path>
47
+ <polygon id="Fill-348" fill="#0073AA" points="12.078 49.372 29.474 49.372 29.474 47.872 12.078 47.872"></polygon>
48
+ <polygon id="Fill-349" fill="#0073AA" points="50.771 7.16 70.439 7.16 70.439 5.66 50.771 5.66"></polygon>
49
+ <polygon id="Fill-350" fill="#0073AA" points="112.441 51.426 150.247 51.426 150.247 49.926 112.441 49.926"></polygon>
50
+ <polygon id="Fill-351" fill="#0073AA" points="30.974 49.372 34.544 49.372 34.544 47.872 30.974 47.872"></polygon>
51
+ <polygon id="Fill-352" fill="#0073AA" points="95.949 13.22 113.346 13.22 113.346 11.72 95.949 11.72"></polygon>
52
+ <polygon id="Fill-353" fill="#0073AA" points="114.846 13.22 118.416 13.22 118.416 11.72 114.846 11.72"></polygon>
53
+ <path d="M129.29,35.307 C129.29,36.051 128.687,36.654 127.943,36.654 C127.2,36.654 126.597,36.051 126.597,35.307 C126.597,34.563 127.2,33.96 127.943,33.96 C128.687,33.96 129.29,34.563 129.29,35.307" id="Fill-354" fill="#0073AA"></path>
54
+ <path d="M60.281,56.195 C60.281,56.939 59.678,57.542 58.934,57.542 C58.19,57.542 57.587,56.939 57.587,56.195 C57.587,55.451 58.19,54.848 58.934,54.848 C59.678,54.848 60.281,55.451 60.281,56.195" id="Fill-355" fill="#0073AA"></path>
55
+ <path d="M149.201,21.202 C149.201,21.616 148.865,21.952 148.451,21.952 C148.037,21.952 147.701,21.616 147.701,21.202 C147.701,20.787 148.037,20.451 148.451,20.451 C148.865,20.451 149.201,20.787 149.201,21.202" id="Fill-356" fill="#0073AA"></path>
56
+ <path d="M57.468,1.665 C57.468,2.227 57.013,2.682 56.451,2.682 C55.889,2.682 55.434,2.227 55.434,1.665 C55.434,1.103 55.889,0.648 56.451,0.648 C57.013,0.648 57.468,1.103 57.468,1.665" id="Fill-357" fill="#0073AA"></path>
57
+ <path d="M122.492,103.665 L47.793,103.665 C43.641,103.665 40.244,100.268 40.244,96.116 L40.244,95.827 C40.244,91.675 43.641,88.278 47.793,88.278 L122.492,88.278 C126.644,88.278 130.041,91.675 130.041,95.827 L130.041,96.116 C130.041,100.268 126.644,103.665 122.492,103.665" id="Fill-358" fill="#FFFFFF"></path>
58
+ <path d="M70.679,76.533 C70.679,83.02 65.421,88.278 58.934,88.278 C52.448,88.278 47.189,83.02 47.189,76.533 C47.189,70.047 52.448,64.789 58.934,64.789 C65.421,64.789 70.679,70.047 70.679,76.533" id="Fill-359" fill="#FFFFFF"></path>
59
+ <path d="M50.772,78.312 C50.772,80.879 48.691,82.96 46.124,82.96 C43.556,82.96 41.475,80.879 41.475,78.312 C41.475,75.744 43.556,73.663 46.124,73.663 C48.691,73.663 50.772,75.744 50.772,78.312" id="Fill-360" fill="#FFFFFF"></path>
60
+ <path d="M48.498,83.603 C48.498,87.387 45.431,90.454 41.648,90.454 C37.865,90.454 34.798,87.387 34.798,83.603 C34.798,79.82 37.865,76.753 41.648,76.753 C45.431,76.753 48.498,79.82 48.498,83.603" id="Fill-361" fill="#FFFFFF"></path>
61
+ <path d="M49.505,94.404 C49.505,99.519 45.359,103.665 40.244,103.665 C35.13,103.665 30.983,99.519 30.983,94.404 C30.983,89.289 35.13,85.143 40.244,85.143 C45.359,85.143 49.505,89.289 49.505,94.404" id="Fill-362" fill="#FFFFFF"></path>
62
+ <path d="M40.244,104.415 C34.725,104.415 30.233,99.924 30.233,94.404 C30.233,91.145 31.828,88.08 34.499,86.205 L35.36,87.434 C33.089,89.027 31.733,91.633 31.733,94.404 C31.733,99.097 35.552,102.915 40.244,102.915 L40.244,104.415" id="Fill-363" fill="#0073AA"></path>
63
+ <path d="M73.306,85.898 C73.306,88.045 71.566,89.785 69.42,89.785 C67.273,89.785 65.533,88.045 65.533,85.898 C65.533,83.752 67.273,82.011 69.42,82.011 C71.566,82.011 73.306,83.752 73.306,85.898" id="Fill-364" fill="#FFFFFF"></path>
64
+ <path d="M120.686,76.042 C120.686,81.948 115.899,86.735 109.993,86.735 C104.088,86.735 99.301,81.948 99.301,76.042 C99.301,70.137 104.088,65.349 109.993,65.349 C115.899,65.349 120.686,70.137 120.686,76.042" id="Fill-365" fill="#FFFFFF"></path>
65
+ <path d="M122.552,82.011 C122.21,85.8 119.49,88.852 115.712,88.852 C111.934,88.852 108.871,85.789 108.871,82.011 C108.871,78.233 111.934,75.171 115.712,75.171 C119.49,75.171 122.892,78.249 122.552,82.011" id="Fill-366" fill="#FFFFFF"></path>
66
+ <path d="M115.7,86.735 C115.357,90.524 112.637,93.576 108.859,93.576 C105.081,93.576 102.019,90.513 102.019,86.735 C102.019,82.957 105.081,79.895 108.859,79.895 C112.637,79.895 116.04,82.973 115.7,86.735" id="Fill-367" fill="#FFFFFF"></path>
67
+ <path d="M68.646,86.824 C67.993,90.612 62.806,93.664 55.601,93.664 C48.397,93.664 42.556,90.602 42.556,86.824 C42.556,83.046 48.397,79.983 55.601,79.983 C62.806,79.983 69.295,83.061 68.646,86.824" id="Fill-368" fill="#FFFFFF"></path>
68
+ <path d="M136.204,83.664 C135.704,89.203 131.727,93.664 126.204,93.664 C120.681,93.664 116.203,89.187 116.203,83.664 C116.203,78.141 120.681,73.663 126.204,73.663 C131.727,73.663 136.702,78.163 136.204,83.664" id="Fill-369" fill="#FFFFFF"></path>
69
+ <path d="M133.476,94.017 C132.993,99.36 129.156,103.665 123.828,103.665 C118.5,103.665 114.18,99.345 114.18,94.017 C114.18,88.689 118.5,84.369 123.828,84.369 C129.156,84.369 133.956,88.71 133.476,94.017" id="Fill-370" fill="#FFFFFF"></path>
70
+ <path d="M107.183,83.126 C107.183,86.594 104.371,89.406 100.903,89.406 C97.435,89.406 94.624,86.594 94.624,83.126 C94.624,79.658 97.435,76.847 100.903,76.847 C104.371,76.847 107.183,79.658 107.183,83.126" id="Fill-371" fill="#FFFFFF"></path>
71
+ <path d="M99.082,88.633 C99.082,90.657 97.441,92.297 95.418,92.297 C93.395,92.297 91.754,90.657 91.754,88.633 C91.754,86.61 93.395,84.969 95.418,84.969 C97.441,84.969 99.082,86.61 99.082,88.633" id="Fill-372" fill="#FFFFFF"></path>
72
+ <path d="M92.195,88.972 L90.695,88.972 C90.695,86.538 92.676,84.558 95.109,84.558 L95.109,86.058 C93.503,86.058 92.195,87.365 92.195,88.972" id="Fill-373" fill="#0073AA"></path>
73
+ <path d="M84.029,90.971 C84.029,93.31 80.759,95.207 76.724,95.207 C72.69,95.207 69.42,93.31 69.42,90.971 C69.42,88.632 72.69,86.735 76.724,86.735 C80.759,86.735 84.029,88.632 84.029,90.971" id="Fill-374" fill="#FFFFFF"></path>
74
+ <path d="M126.407,73.973 C126.407,76.69 121.797,83.938 121.797,80.606 C121.797,77.89 116.879,78.404 116.879,75.688 C116.879,72.971 119.081,70.769 121.797,70.769 C124.514,70.769 126.407,71.257 126.407,73.973" id="Fill-375" fill="#FFFFFF"></path>
75
+ <path d="M125.598,73.972 C124.924,72.481 123.432,71.519 121.797,71.519 C121.219,71.519 120.657,71.635 120.13,71.865 L119.53,70.49 C120.248,70.178 121.011,70.019 121.797,70.019 C124.019,70.019 126.049,71.328 126.965,73.355 L125.598,73.972" id="Fill-376" fill="#0073AA"></path>
76
+ <path d="M101.902,84.134 C99.741,81.973 98.551,79.099 98.551,76.042 C98.551,72.984 99.741,70.111 101.902,67.951 C104.063,65.79 106.935,64.6 109.993,64.6 C113.05,64.6 115.924,65.79 118.085,67.951 C120.246,70.112 121.436,72.985 121.436,76.042 L119.936,76.042 C119.936,73.387 118.902,70.89 117.024,69.012 C115.146,67.134 112.649,66.1 109.993,66.1 C107.337,66.1 104.84,67.134 102.963,69.012 C101.085,70.889 100.051,73.386 100.051,76.042 C100.051,78.698 101.085,81.195 102.963,83.073 L101.902,84.134" id="Fill-377" fill="#0073AA"></path>
77
+ <polygon id="Fill-378" fill="#FFFFFF" points="77.451 51.464 89.899 51.464 89.899 46.22 77.451 46.22"></polygon>
78
+ <path d="M78.201,50.714 L89.148,50.714 L89.148,46.97 L78.201,46.97 L78.201,50.714 Z M76.701,52.214 L90.648,52.214 L90.648,45.47 L76.701,45.47 L76.701,52.214 Z" id="Fill-379" fill="#0073AA"></path>
79
+ <path d="M64.551,46.294 L76.328,46.294 L76.328,20.645 L71.981,26.524 C70.731,28.215 69.261,29.72 67.751,31.184 C65.729,33.146 64.551,35.865 64.551,38.748 L64.551,46.294" id="Fill-380" fill="#ADDDF8"></path>
80
+ <path d="M75.413,45.544 L72.563,45.544 L72.563,26.999 C72.57,26.989 72.578,26.98 72.585,26.97 L75.413,23.144 L75.413,45.544" id="Fill-381" fill="#8AC4E6"></path>
81
+ <path d="M102.228,46.785 L90.451,46.785 L90.451,21.136 L94.797,27.015 C96.048,28.706 97.517,30.211 99.027,31.675 C101.05,33.637 102.228,36.356 102.228,39.239 L102.228,46.785" id="Fill-382" fill="#ADDDF8"></path>
82
+ <polyline id="Fill-383" fill="#A6CFE4" points="94.578 26.718 91.618 22.714 91.618 21.328 94.578 25.331 94.578 26.718"></polyline>
83
+ <polyline id="Fill-384" fill-opacity="0.35266644" fill="#4B97C6" points="94.578 45.889 91.618 45.889 91.618 22.714 94.578 26.718 94.578 45.889"></polyline>
84
+ <path d="M65.301,45.544 L75.577,45.544 L75.577,22.922 L72.585,26.97 C71.271,28.745 69.736,30.303 68.274,31.722 C66.385,33.554 65.301,36.115 65.301,38.748 L65.301,45.544 M77.077,47.044 L63.801,47.044 L63.801,38.748 C63.801,35.712 65.051,32.758 67.229,30.645 C68.647,29.27 70.13,27.766 71.379,26.077 L77.077,18.369 L77.077,47.044" id="Fill-385" fill="#0073AA"></path>
85
+ <path d="M91.201,45.544 L101.477,45.544 L101.477,38.748 C101.477,36.115 100.394,33.555 98.505,31.722 C97.043,30.304 95.509,28.747 94.194,26.969 L91.201,22.921 L91.201,45.544 Z M102.977,47.044 L89.701,47.044 L89.701,18.37 L95.401,26.077 C96.65,27.769 98.133,29.272 99.55,30.646 C101.728,32.759 102.977,35.712 102.977,38.748 L102.977,47.044 L102.977,47.044 Z" id="Fill-386" fill="#0073AA"></path>
86
+ <path d="M91.618,48.85 L75.413,48.85 L75.413,15.269 C75.413,11.35 77.228,7.653 80.329,5.258 L81.776,4.14 C82.8,3.349 84.23,3.349 85.255,4.14 L86.833,5.36 C89.851,7.691 91.618,11.289 91.618,15.102 L91.618,48.85" id="Fill-387" fill="#FFFFFF"></path>
87
+ <path d="M79.37,6.274 L81.3,4.784 C82.666,3.728 84.573,3.728 85.939,4.784 L88.045,6.41 L79.37,6.274" id="Fill-388" fill="#0073AA"></path>
88
+ <path d="M75.413,15.269 C75.413,11.35 77.228,7.653 80.329,5.258 L81.776,4.14 C82.288,3.745 82.902,3.547 83.515,3.547 C84.129,3.547 84.742,3.745 85.255,4.14 C84.742,3.745 84.129,3.547 83.515,3.547 C82.902,3.547 82.288,3.745 81.776,4.14 L80.329,5.258 C77.228,7.653 75.413,11.35 75.413,15.269 M85.296,4.172 L85.255,4.14 L85.296,4.172" id="Fill-389" fill="#AFC9D6"></path>
89
+ <path d="M78.974,48.85 L75.413,48.85 L75.413,15.269 C75.413,11.35 77.228,7.653 80.329,5.258 L81.776,4.14 C82.288,3.745 82.902,3.547 83.515,3.547 C84.129,3.547 84.742,3.745 85.255,4.14 L85.296,4.172 L85.126,4.303 C84.646,4.096 84.133,3.992 83.62,3.992 C82.801,3.992 81.983,4.256 81.3,4.784 L79.37,6.274 L82.677,6.326 C80.321,8.682 78.974,11.893 78.974,15.269 L78.974,48.85" id="Fill-390" fill="#E7F7FF"></path>
90
+ <path d="M82.677,6.326 L79.37,6.274 L81.3,4.784 C81.983,4.256 82.801,3.992 83.62,3.992 C84.133,3.992 84.646,4.096 85.126,4.303 L83.89,5.258 C83.461,5.59 83.056,5.947 82.677,6.326" id="Fill-391" fill="#0073AA"></path>
91
+ <path d="M88.386,22.231 L86.886,22.231 C86.886,20.373 85.374,18.861 83.516,18.861 C81.657,18.861 80.144,20.373 80.144,22.231 L78.644,22.231 C78.644,19.546 80.83,17.361 83.516,17.361 C86.201,17.361 88.386,19.546 88.386,22.231" id="Fill-392" fill="#0073AA"></path>
92
+ <path d="M76.163,48.1 L90.868,48.1 L90.868,15.103 C90.868,11.547 89.188,8.127 86.374,5.953 L84.797,4.734 C84.041,4.15 82.989,4.15 82.235,4.734 L80.787,5.852 C77.892,8.089 76.163,11.609 76.163,15.269 L76.163,48.1 Z M92.368,49.6 L74.663,49.6 L74.663,15.269 C74.663,11.148 76.609,7.184 79.87,4.664 L81.318,3.547 C82.611,2.547 84.419,2.547 85.714,3.547 L87.291,4.766 C90.471,7.222 92.368,11.086 92.368,15.103 L92.368,49.6 L92.368,49.6 Z" id="Fill-393" fill="#0073AA"></path>
93
+ <path d="M100.903,90.155 C97.027,90.155 93.874,87.002 93.874,83.126 C93.874,79.936 96.026,77.141 99.107,76.328 L99.49,77.779 C97.066,78.418 95.374,80.616 95.374,83.126 C95.374,86.175 97.854,88.655 100.903,88.655 L100.903,90.155" id="Fill-394" fill="#0073AA"></path>
94
+ <path d="M65.124,87.39 L64.38,86.087 C67.803,84.131 69.929,80.471 69.929,76.533 C69.929,70.471 64.997,65.539 58.935,65.539 C52.872,65.539 47.939,70.471 47.939,76.533 L46.439,76.533 C46.439,69.644 52.045,64.039 58.935,64.039 C65.824,64.039 71.429,69.644 71.429,76.533 C71.429,81.008 69.013,85.167 65.124,87.39" id="Fill-395" fill="#0073AA"></path>
95
+ <path d="M44.285,74.873 L43.578,73.551 C44.833,72.879 46.358,72.732 47.716,73.152 L47.274,74.586 C46.293,74.284 45.191,74.389 44.285,74.873" id="Fill-396" fill="#0073AA"></path>
96
+ <path d="M69.42,90.535 L69.42,89.035 C71.149,89.035 72.557,87.628 72.557,85.898 C72.557,84.169 71.149,82.762 69.42,82.762 L69.42,81.262 C71.977,81.262 74.057,83.342 74.057,85.898 C74.057,88.455 71.977,90.535 69.42,90.535" id="Fill-397" fill="#0073AA"></path>
97
+ <path d="M99.033,94.918 L71.351,94.918 L79.363,85.898 L79.363,52.557 L87.943,52.557 L87.574,82.994 C87.551,84.824 88.383,86.56 89.823,87.691 L99.033,94.918" id="Fill-398" fill="#FFFFFF"></path>
98
+ <path d="M92.003,88.408 L91.112,88.408 C88.865,88.386 87.044,86.546 87.044,84.307 L87.044,52.557 L88.544,52.557 L88.544,84.307 C88.544,85.727 89.699,86.894 91.12,86.908 L92.003,86.908 L92.003,88.408" id="Fill-399" fill="#0073AA"></path>
99
+ <path d="M76.339,87.574 L73.298,87.574 L73.298,86.074 L76.339,86.074 C77.593,86.074 78.613,85.054 78.613,83.8 L78.613,52.557 L80.113,52.557 L80.113,83.8 C80.113,85.881 78.42,87.574 76.339,87.574" id="Fill-400" fill="#0073AA"></path>
100
+ <path d="M74.85,101.262 C74.842,100.523 74.977,99.794 75.249,99.099 C75.521,98.405 75.919,97.781 76.43,97.243 C76.96,96.687 77.588,96.247 78.298,95.938 C79.008,95.628 79.758,95.467 80.527,95.459 C81.263,95.461 81.996,95.585 82.69,95.857 C83.385,96.132 84.009,96.529 84.546,97.039 L83.513,98.127 C83.116,97.75 82.654,97.456 82.141,97.253 C81.643,97.058 81.12,96.959 80.588,96.959 L80.543,96.959 C79.976,96.965 79.422,97.084 78.897,97.313 C78.373,97.542 77.908,97.866 77.517,98.277 C77.141,98.673 76.847,99.135 76.645,99.648 C76.443,100.162 76.344,100.7 76.35,101.246 L74.85,101.262" id="Fill-401" fill="#0073AA"></path>
101
+ <path d="M50.021,89.266 C50.014,88.524 50.148,87.797 50.421,87.103 C50.694,86.408 51.092,85.783 51.603,85.246 C52.134,84.689 52.763,84.249 53.472,83.941 C54.18,83.632 54.93,83.471 55.699,83.463 C56.45,83.464 57.17,83.589 57.862,83.861 C58.555,84.134 59.179,84.531 59.718,85.043 L58.685,86.131 C58.286,85.753 57.825,85.459 57.313,85.258 C56.801,85.056 56.251,84.946 55.715,84.963 C55.147,84.969 54.594,85.088 54.07,85.316 C53.546,85.544 53.081,85.868 52.69,86.28 C52.313,86.677 52.019,87.138 51.817,87.651 C51.615,88.165 51.516,88.703 51.521,89.25 L50.021,89.266" id="Fill-402" fill="#0073AA"></path>
102
+ <path d="M114.568,86.589 L113.164,86.064 C113.355,85.551 113.444,85.012 113.428,84.461 C113.413,83.912 113.293,83.378 113.072,82.874 C112.846,82.356 112.519,81.895 112.102,81.5 C111.686,81.107 111.206,80.808 110.674,80.609 C110.161,80.417 109.619,80.319 109.07,80.345 C108.521,80.359 107.987,80.48 107.483,80.7 L106.882,79.325 C107.564,79.027 108.286,78.865 109.028,78.845 C109.77,78.831 110.502,78.944 111.198,79.204 C111.919,79.473 112.57,79.879 113.133,80.41 C113.695,80.942 114.138,81.569 114.446,82.272 C114.745,82.955 114.907,83.677 114.928,84.419 C114.949,85.163 114.828,85.894 114.568,86.589" id="Fill-403" fill="#0073AA"></path>
103
+ <path d="M81.966,96.485 L80.479,96.284 C80.583,95.52 80.835,94.795 81.228,94.131 C81.62,93.468 82.134,92.898 82.753,92.439 C83.352,91.996 84.019,91.679 84.739,91.493 C85.463,91.31 86.201,91.266 86.935,91.363 L86.736,92.85 C86.194,92.776 85.647,92.809 85.112,92.946 C84.581,93.083 84.087,93.318 83.644,93.645 C83.188,93.982 82.81,94.403 82.52,94.895 C82.228,95.387 82.042,95.921 81.966,96.485" id="Fill-404" fill="#0073AA"></path>
104
+ <path d="M114.923,97.504 L113.436,97.303 C113.539,96.541 113.791,95.817 114.185,95.15 C114.579,94.484 115.092,93.915 115.71,93.457 C116.307,93.016 116.975,92.698 117.696,92.512 C118.42,92.326 119.158,92.284 119.893,92.382 L119.693,93.868 C119.151,93.794 118.603,93.826 118.069,93.965 C117.536,94.103 117.042,94.337 116.602,94.662 C116.146,95 115.768,95.421 115.477,95.913 C115.185,96.406 114.999,96.941 114.923,97.504" id="Fill-405" fill="#0073AA"></path>
105
+ <path d="M97.82,104.67 C97.09,104.43 96.424,104.05 95.841,103.542 C95.258,103.033 94.791,102.424 94.454,101.733 C94.128,101.065 93.938,100.35 93.888,99.607 C93.836,98.865 93.928,98.131 94.159,97.425 L95.585,97.892 C95.414,98.413 95.346,98.956 95.384,99.505 C95.421,100.055 95.561,100.582 95.802,101.075 C96.051,101.586 96.395,102.035 96.827,102.411 C97.258,102.787 97.75,103.067 98.289,103.244 L97.82,104.67" id="Fill-406" fill="#0073AA" transform="translate(96.081444, 101.047500) rotate(-7.000000) translate(-96.081444, -101.047500) "></path>
106
+ <path d="M36.273,88.978 C34.838,87.541 34.048,85.633 34.048,83.604 C34.048,79.412 37.457,76.003 41.648,76.003 C43.405,76.003 45.119,76.617 46.475,77.731 L45.521,78.89 C44.435,77.995 43.059,77.503 41.648,77.503 C38.284,77.503 35.548,80.239 35.548,83.604 C35.548,85.232 36.183,86.765 37.334,87.917 L36.273,88.978" id="Fill-407" fill="#0073AA"></path>
107
+ <path d="M130.726,93.384 L130.065,92.036 C133.054,90.573 135.119,87.34 135.457,83.597 C135.655,81.409 134.9,79.259 133.331,77.541 C131.048,75.044 127.321,73.874 124.019,74.673 L123.668,73.214 C127.486,72.293 131.796,73.638 134.438,76.529 C136.295,78.563 137.188,81.12 136.951,83.731 C136.56,88.058 134.233,91.666 130.726,93.384" id="Fill-408" fill="#0073AA"></path>
108
+ <path d="M123.828,104.415 L123.828,102.915 C128.517,102.915 132.26,99.145 132.728,93.949 C132.815,92.994 132.716,92.046 132.433,91.133 L133.864,90.688 C134.207,91.79 134.327,92.933 134.223,94.085 C133.683,100.07 129.31,104.415 123.828,104.415" id="Fill-409" fill="#0073AA"></path>
109
+ <path d="M43.833,81.368 L17.011,81.368 C16.226,81.368 15.59,82.004 15.59,82.788 L15.59,103.676 L42.442,103.676 C44.033,103.676 45.323,102.386 45.323,100.794 L45.323,82.858 C45.323,82.035 44.656,81.368 43.833,81.368" id="Fill-410" fill="#62A6CF"></path>
110
+ <path d="M42.988,104.426 L14.841,104.426 L14.841,82.519 C14.841,81.471 15.693,80.618 16.741,80.618 L34.269,80.618 L34.269,82.118 L16.741,82.118 C16.52,82.118 16.341,82.298 16.341,82.519 L16.341,102.926 L42.988,102.926 C43.862,102.926 44.573,102.215 44.573,101.341 L44.573,82.576 C44.573,82.323 44.368,82.118 44.115,82.118 L42.284,82.118 L42.284,80.618 L44.115,80.618 C45.195,80.618 46.073,81.496 46.073,82.576 L46.073,101.341 C46.073,103.042 44.689,104.426 42.988,104.426" id="Fill-411" fill="#0073AA"></path>
111
+ <path d="M34.673,73.02 L40.596,78.943 L40.596,99.62 C40.596,100.556 39.837,101.315 38.902,101.315 L21.218,101.315 C20.283,101.315 19.524,100.556 19.524,99.62 L19.524,74.714 C19.524,73.778 20.283,73.02 21.218,73.02 L34.673,73.02" id="Fill-412" fill="#FFFFFF"></path>
112
+ <polyline id="Fill-413" fill="#62A6CF" points="34.673 78.943 34.673 73.02 40.596 78.943 34.673 78.943"></polyline>
113
+ <path d="M40.596,79.692 L35.265,79.692 C34.524,79.692 33.923,79.091 33.923,78.351 L33.923,73.02 L35.423,73.02 L35.423,78.192 L40.596,78.192 L40.596,79.692" id="Fill-414" fill="#0073AA"></path>
114
+ <path d="M38.901,102.065 L21.219,102.065 L21.219,100.565 L38.901,100.565 C39.422,100.565 39.846,100.141 39.846,99.62 L39.846,79.253 L34.362,73.77 L21.219,73.77 C20.697,73.77 20.273,74.193 20.273,74.714 L20.273,82.695 L18.773,82.695 L18.773,74.714 C18.773,73.366 19.87,72.27 21.219,72.27 L34.983,72.27 L41.346,78.632 L41.346,99.62 C41.346,100.968 40.249,102.065 38.901,102.065" id="Fill-415" fill="#0073AA"></path>
115
+ <path d="M38.123,71.295 L44.046,77.218 L44.046,97.895 C44.046,98.831 43.287,99.59 42.351,99.59 L24.668,99.59 C23.732,99.59 22.974,98.831 22.974,97.895 L22.974,72.989 C22.974,72.053 23.732,71.295 24.668,71.295 L38.123,71.295" id="Fill-416" fill="#ADDDF8"></path>
116
+ <polyline id="Fill-417" fill="#62A6CF" points="38.123 77.218 38.123 71.295 44.046 77.218 38.123 77.218"></polyline>
117
+ <path d="M44.046,77.968 L38.715,77.968 C37.975,77.968 37.373,77.366 37.373,76.626 L37.373,71.295 L38.873,71.295 L38.873,76.468 L44.046,76.468 L44.046,77.968" id="Fill-418" fill="#0073AA"></path>
118
+ <polygon id="Fill-419" fill="#0073AA" points="26.133 80.857 41.201 80.857 41.201 79.357 26.133 79.357"></polygon>
119
+ <polygon id="Fill-420" fill="#0073AA" points="26.133 84.218 41.201 84.218 41.201 82.718 26.133 82.718"></polygon>
120
+ <polygon id="Fill-421" fill="#0073AA" points="26.133 87.577 41.201 87.577 41.201 86.077 26.133 86.077"></polygon>
121
+ <polygon id="Fill-422" fill="#0073AA" points="26.133 90.938 41.201 90.938 41.201 89.438 26.133 89.438"></polygon>
122
+ <path d="M42.352,100.34 L41.572,100.34 L41.572,98.84 L42.352,98.84 C42.872,98.84 43.296,98.416 43.296,97.896 L43.296,77.528 L37.813,72.045 L24.668,72.045 C24.147,72.045 23.724,72.469 23.724,72.989 L23.724,84.941 L22.224,84.941 L22.224,72.989 C22.224,71.642 23.32,70.545 24.668,70.545 L38.434,70.545 L44.796,76.907 L44.796,97.896 C44.796,99.243 43.699,100.34 42.352,100.34" id="Fill-423" fill="#0073AA"></path>
123
+ <path d="M41.572,73.882 L47.496,79.805 L47.496,100.483 C47.496,101.418 46.737,102.177 45.801,102.177 L28.118,102.177 C27.182,102.177 26.423,101.418 26.423,100.483 L26.423,75.577 C26.423,74.641 27.182,73.882 28.118,73.882 L41.572,73.882" id="Fill-424" fill="#FFFFFF"></path>
124
+ <polyline id="Fill-425" fill="#E7F7FF" points="41.572 79.805 41.572 73.882 47.496 79.805 41.572 79.805"></polyline>
125
+ <path d="M47.495,80.556 L42.164,80.556 C41.424,80.556 40.822,79.953 40.822,79.213 L40.822,73.882 L42.322,73.882 L42.322,79.056 L47.495,79.056 L47.495,80.556" id="Fill-426" fill="#0073AA"></path>
126
+ <polygon id="Fill-427" fill="#0073AA" points="29.582 83.445 44.651 83.445 44.651 81.945 29.582 81.945"></polygon>
127
+ <polygon id="Fill-428" fill="#0073AA" points="29.582 86.805 44.651 86.805 44.651 85.305 29.582 85.305"></polygon>
128
+ <polygon id="Fill-429" fill="#0073AA" points="29.582 90.165 44.651 90.165 44.651 88.665 29.582 88.665"></polygon>
129
+ <polygon id="Fill-430" fill="#0073AA" points="29.582 93.525 44.651 93.525 44.651 92.025 29.582 92.025"></polygon>
130
+ <path d="M45.801,102.927 L42.779,102.927 L42.779,101.427 L45.801,101.427 C46.321,101.427 46.745,101.003 46.745,100.482 L46.745,80.116 L41.262,74.632 L28.118,74.632 C27.598,74.632 27.174,75.056 27.174,75.576 L27.174,84.942 L25.674,84.942 L25.674,75.576 C25.674,74.229 26.77,73.132 28.118,73.132 L41.883,73.132 L48.245,79.495 L48.245,100.482 C48.245,101.83 47.148,102.927 45.801,102.927" id="Fill-431" fill="#0073AA"></path>
131
+ <path d="M42.943,103.676 L14.569,103.676 C13.889,103.676 13.337,103.124 13.337,102.444 L13.337,84.829 C13.337,84.26 13.798,83.8 14.366,83.8 L19.216,83.8 C19.569,83.8 19.898,83.982 20.086,84.281 L21.338,86.27 L39.383,86.27 C40.034,86.27 40.562,86.798 40.562,87.449 L40.562,101.296 C40.562,102.407 41.358,103.676 42.943,103.676" id="Fill-432" fill="#FFFFFF"></path>
132
+ <path d="M40.562,97.998 L13.337,97.998 L13.337,102.081085 C13.337,102.961395 13.889,103.676 14.57,103.676 L42.943,103.676 C41.358,103.676 40.562,102.033186 40.562,100.59362 L40.562,97.998 Z" id="Fill-433" fill="#E7F7FF"></path>
133
+ <path d="M42.942,104.426 C40.909,104.426 39.813,102.814 39.813,101.296 L39.813,87.449 C39.813,87.212 39.62,87.02 39.384,87.02 L20.924,87.02 L19.452,84.68 C19.401,84.6 19.311,84.55 19.216,84.55 L14.366,84.55 C14.212,84.55 14.087,84.675 14.087,84.829 L14.087,103.571 L12.587,103.571 L12.587,84.829 C12.587,83.848 13.385,83.05 14.366,83.05 L19.216,83.05 C19.83,83.05 20.394,83.36 20.722,83.88 L21.752,85.52 L39.384,85.52 C40.447,85.52 41.313,86.385 41.313,87.449 L41.313,101.296 C41.313,101.951 41.746,102.926 42.942,102.926 L42.942,104.426" id="Fill-434" fill="#0073AA"></path>
134
+ <polygon id="Fill-435" fill="#0073AA" points="5.332 104.426 164.92275 104.426 164.92275 102.926 5.332 102.926"></polygon>
135
+ <polygon id="Fill-436" fill="#0073AA" points="0.77 104.426 3.509 104.426 3.509 102.926 0.77 102.926"></polygon>
136
+ </g>
137
+ </g>
138
+ </g>
139
+ </symbol>
140
+
141
+
142
+ <symbol id="icon-done" width="177px" height="107px" viewBox="0 0 177 107" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
143
+ <!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
144
+ <desc>Created with Sketch.</desc>
145
+ <defs></defs>
146
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
147
+ <path d="M62.855,71.007 L62.759,71.007 C62.336,69.55 60.994,68.484 59.401,68.484 C58.705,68.484 58.059,68.689 57.514,69.039 C57.16,66.744 55.182,64.984 52.788,64.984 C50.638,64.984 48.82,66.4 48.215,68.35 C47.942,68.277 47.656,68.234 47.36,68.234 C45.72,68.234 44.364,69.436 44.116,71.007 L43.58,71.007 C42.23,71.007 41.125,72.112 41.125,73.462 L41.125,73.643 C41.125,74.993 42.23,76.098 43.58,76.098 L62.855,76.098 C64.205,76.098 65.31,74.993 65.31,73.643 L65.31,73.462 C65.31,72.112 64.205,71.007 62.855,71.007" id="Fill-447" fill="#FFFFFF"></path>
148
+ <path d="M52.734,76.848 L43.552,76.848 C41.603,76.848 40.017,75.262 40.017,73.313 L40.017,73.108 C40.017,71.159 41.603,69.573 43.552,69.573 L43.565,69.573 C44.185,67.566 46.144,66.208 48.328,66.456 C48.409,66.268 48.501,66.083 48.602,65.904 C48.789,65.567 49.014,65.243 49.268,64.941 C50.447,63.543 52.171,62.741 53.997,62.741 C57.065,62.741 59.632,64.943 60.1,67.977 L58.617,68.205 C58.263,65.908 56.32,64.241 53.997,64.241 C52.614,64.241 51.309,64.848 50.415,65.907 C50.223,66.136 50.053,66.381 49.911,66.636 C49.75,66.923 49.621,67.223 49.526,67.531 L49.314,68.222 L48.614,68.034 C46.835,67.552 45.17,68.734 44.9,70.44 L44.8,71.073 L43.552,71.073 C42.43,71.073 41.517,71.986 41.517,73.108 L41.517,73.313 C41.517,74.435 42.43,75.348 43.552,75.348 L52.734,75.348 L52.734,76.848" id="Fill-449" fill="#0073AA"></path>
149
+ <path d="M50.593,69.095 L49.093,69.095 C49.093,68.442 48.561,67.912 47.909,67.912 L47.909,66.412 C49.389,66.412 50.593,67.615 50.593,69.095" id="Fill-450" fill="#0073AA"></path>
150
+ <path d="M118.93,55.565 C118.058,54.222 116.583,53.42 114.986,53.42 C114.142,53.42 113.313,53.647 112.59,54.076 L111.824,52.786 C112.779,52.22 113.872,51.92 114.986,51.92 C117.094,51.92 119.038,52.978 120.188,54.749 L118.93,55.565" id="Fill-453" fill="#0073AA"></path>
151
+ <path d="M129.06,66.089 L116.041,66.089 L116.041,64.589 L129.06,64.589 C130.497,64.589 131.667,63.42 131.667,61.982 L131.667,61.735 C131.667,60.297 130.497,59.128 129.06,59.128 L128.365,59.128 L128.208,58.587 C127.712,56.874 126.119,55.679 124.337,55.679 C123.566,55.679 122.814,55.9 122.162,56.319 L121.191,56.941 L121.016,55.802 C120.577,52.958 118.172,50.894 115.295,50.894 C113.582,50.894 111.966,51.646 110.861,52.957 C110.624,53.238 110.414,53.542 110.235,53.861 C110.037,54.214 109.877,54.587 109.76,54.968 L109.546,55.658 L108.848,55.47 C108.52,55.382 108.193,55.337 107.875,55.337 L107.875,53.837 C108.102,53.837 108.33,53.853 108.559,53.884 C108.666,53.626 108.789,53.373 108.927,53.128 C109.151,52.727 109.416,52.344 109.715,51.99 C111.104,50.34 113.139,49.394 115.295,49.394 C118.572,49.394 121.364,51.521 122.274,54.579 C122.928,54.315 123.627,54.179 124.337,54.179 C126.598,54.179 128.635,55.581 129.469,57.648 C131.542,57.854 133.167,59.608 133.167,61.735 L133.167,61.982 C133.167,64.247 131.324,66.089 129.06,66.089" id="Fill-454" fill="#0073AA"></path>
152
+ <path d="M120.512,17.724 L116.237,17.724 C115.823,17.724 115.487,17.388 115.487,16.974 C115.487,16.56 115.823,16.224 116.237,16.224 L120.512,16.224 C120.926,16.224 121.262,16.56 121.262,16.974 C121.262,17.388 120.926,17.724 120.512,17.724" id="Fill-456" fill="#0073AA"></path>
153
+ <path d="M118.375,19.861 C117.961,19.861 117.625,19.525 117.625,19.111 L117.625,14.837 C117.625,14.423 117.961,14.087 118.375,14.087 C118.789,14.087 119.125,14.423 119.125,14.837 L119.125,19.111 C119.125,19.525 118.789,19.861 118.375,19.861" id="Fill-457" fill="#0073AA"></path>
154
+ <path d="M43.262,17.724 L38.987,17.724 C38.573,17.724 38.237,17.388 38.237,16.974 C38.237,16.56 38.573,16.224 38.987,16.224 L43.262,16.224 C43.676,16.224 44.012,16.56 44.012,16.974 C44.012,17.388 43.676,17.724 43.262,17.724" id="Fill-458" fill="#0073AA"></path>
155
+ <path d="M41.125,19.861 C40.711,19.861 40.375,19.525 40.375,19.111 L40.375,14.837 C40.375,14.423 40.711,14.087 41.125,14.087 C41.539,14.087 41.875,14.423 41.875,14.837 L41.875,19.111 C41.875,19.525 41.539,19.861 41.125,19.861" id="Fill-459" fill="#0073AA"></path>
156
+ <path d="M167.776,59.179 C167.584,59.179 167.393,59.106 167.246,58.96 C166.953,58.667 166.953,58.192 167.246,57.899 L170.269,54.877 C170.561,54.583 171.036,54.583 171.329,54.877 C171.622,55.169 171.622,55.644 171.329,55.937 L168.307,58.96 C168.16,59.106 167.969,59.179 167.776,59.179" id="Fill-460" fill="#0073AA"></path>
157
+ <path d="M170.799,59.179 C170.606,59.179 170.415,59.106 170.269,58.96 L167.246,55.937 C166.953,55.644 166.953,55.169 167.246,54.877 C167.539,54.583 168.014,54.583 168.307,54.877 L171.329,57.899 C171.622,58.192 171.622,58.667 171.329,58.96 C171.183,59.106 170.991,59.179 170.799,59.179" id="Fill-461" fill="#0073AA"></path>
158
+ <polygon id="Fill-466" fill="#0073AA" points="52.286 13.45 93.083 13.45 93.083 11.95 52.286 11.95"></polygon>
159
+ <polygon id="Fill-467" fill="#0073AA" points="9.023 59.856 34.619 59.856 34.619 58.356 9.023 58.356"></polygon>
160
+ <polygon id="Fill-468" fill="#0073AA" points="142.007 67.75 161.109 67.75 161.109 66.25 142.007 66.25"></polygon>
161
+ <polygon id="Fill-469" fill="#0073AA" points="163.403 67.75 166.009 67.75 166.009 66.25 163.403 66.25"></polygon>
162
+ <path d="M93.102,28.199 C93.102,28.939 92.502,29.538 91.763,29.538 C91.024,29.538 90.425,28.939 90.425,28.199 C90.425,27.46 91.024,26.861 91.763,26.861 C92.502,26.861 93.102,27.46 93.102,28.199" id="Fill-471" fill="#0073AA"></path>
163
+ <path d="M161.822,49.274 C161.822,50.147 161.114,50.855 160.241,50.855 C159.367,50.855 158.659,50.147 158.659,49.274 C158.659,48.401 159.367,47.693 160.241,47.693 C161.114,47.693 161.822,48.401 161.822,49.274" id="Fill-472" fill="#0073AA"></path>
164
+ <path d="M141.313,34.272 L147.564,34.272 L147.586,33.937 C147.722,31.815 150.68,28.247 152.484,26.167 C154.733,23.575 156.095,20.192 156.095,16.49 C156.095,8.326 149.477,1.708 141.313,1.708 L141.313,34.272" id="Fill-474" fill="#E7F7FF"></path>
165
+ <path d="M141.309,34.272 L135.058,34.272 L135.037,33.937 C134.9,31.815 131.941,28.247 130.137,26.167 C127.888,23.575 126.526,20.192 126.526,16.49 C126.526,8.326 133.144,1.708 141.308,1.708 L141.309,34.272" id="Fill-475" fill="#FFFFFF"></path>
166
+ <path d="M141.211,34.272 L144.772,34.272 L144.784,33.937 C144.86,31.815 146.513,28.247 147.541,26.167 C148.822,23.575 149.598,20.192 149.598,16.49 C149.598,8.326 145.827,1.708 141.176,1.708 L141.211,34.272" id="Fill-476" fill="#FFFFFF"></path>
167
+ <path d="M141.208,34.272 L137.647,34.272 L137.634,33.937 C137.554,31.815 135.835,28.247 134.807,26.167 C133.526,23.575 132.75,20.192 132.75,16.49 C132.75,8.326 136.521,1.708 141.173,1.708 L141.208,34.272" id="Fill-477" fill="#E7F7FF"></path>
168
+ <path d="M141.96,33.522 L144.062,33.522 C144.235,31.818 145.157,29.296 146.869,25.835 C148.146,23.251 148.849,19.933 148.849,16.49 C148.849,9.346 145.749,3.243 141.927,2.528 L141.96,33.522 Z M145.497,35.022 L140.462,35.022 L140.425,0.957 L141.176,0.957 C146.319,0.957 150.349,7.78 150.349,16.49 C150.349,20.158 149.59,23.713 148.214,26.5 C146.064,30.848 145.569,32.959 145.534,33.964 L145.497,35.022 L145.497,35.022 Z" id="Fill-478" fill="#0073AA"></path>
169
+ <path d="M138.354,33.522 L140.457,33.522 L140.424,2.524 C136.543,3.215 133.5,9.215 133.5,16.49 C133.5,19.935 134.203,23.253 135.479,25.835 C136.788,28.483 138.133,31.436 138.354,33.522 Z M141.959,35.022 L136.925,35.022 L136.885,33.966 C136.807,31.907 134.935,28.118 134.135,26.5 C132.759,23.715 132,20.161 132,16.49 C132,7.78 136.029,0.957 141.173,0.957 L141.922,0.957 L141.959,35.022 L141.959,35.022 Z" id="Fill-479" fill="#0073AA"></path>
170
+ <path d="M135.731,33.522 L146.881,33.522 C147.175,31.771 148.781,29.27 151.891,25.706 C154.118,23.154 155.345,19.881 155.345,16.49 C155.345,12.573 153.771,8.939 150.914,6.257 C148.06,3.578 144.325,2.24 140.399,2.487 C133.477,2.925 127.855,8.467 127.324,15.378 C127.031,19.185 128.244,22.855 130.74,25.712 C133.7,29.1 135.375,31.723 135.731,33.522 Z M147.354,35.022 L135.273,35.022 C134.758,35.022 134.33,34.618 134.3,34.103 C134.21,32.708 132.501,30.007 129.611,26.699 C126.848,23.536 125.504,19.475 125.828,15.263 C126.417,7.611 132.641,1.475 140.306,0.99 C144.632,0.714 148.781,2.197 151.941,5.163 C155.058,8.089 156.845,12.217 156.845,16.49 C156.845,20.243 155.487,23.867 153.021,26.693 C149.258,31.005 148.396,33.039 148.334,33.986 L148.326,34.107 C148.293,34.621 147.865,35.022 147.354,35.022 L147.354,35.022 Z" id="Fill-480" fill="#0073AA"></path>
171
+ <path d="M142.856,53.914 L140.071,53.914 C137.876,53.914 136.08,52.118 136.08,49.923 L136.08,44.851 L146.847,44.851 L146.847,49.923 C146.847,52.118 145.051,53.914 142.856,53.914" id="Fill-481" fill="#8AC4E6"></path>
172
+ <path d="M137.51,49.923 L137.51,44.851 L136.08,44.851 L136.08,49.923 C136.08,52.118 137.876,53.914 140.071,53.914 L141.501,53.914 C139.306,53.914 137.51,52.118 137.51,49.923" id="Fill-482" fill="#E7F7FF"></path>
173
+ <path d="M144.395,44.851 L144.395,49.923 C144.395,52.118 142.599,53.914 140.404,53.914 L142.856,53.914 C145.051,53.914 146.847,52.118 146.847,49.923 L146.847,44.851 L144.395,44.851" id="Fill-483" fill="#E7F7FF"></path>
174
+ <polyline id="Fill-484" fill="#468EBF" points="137.51 47.072 137.51 44.851 144.395 44.851 144.395 46.106 137.51 47.072"></polyline>
175
+ <path d="M136.829,45.601 L136.829,49.923 C136.829,51.71 138.283,53.164 140.07,53.164 L142.856,53.164 C144.644,53.164 146.098,51.71 146.098,49.923 L146.098,45.601 L136.829,45.601 Z M142.856,54.664 L140.07,54.664 C137.456,54.664 135.329,52.537 135.329,49.923 L135.329,44.101 L147.598,44.101 L147.598,49.923 C147.598,52.537 145.471,54.664 142.856,54.664 L142.856,54.664 Z" id="Fill-487" fill="#0073AA"></path>
176
+ <path d="M147.265,44.788 L135.662,44.788 C135.113,44.788 134.664,44.339 134.664,43.79 L134.664,43.789 C134.664,43.24 135.113,42.791 135.662,42.791 L147.265,42.791 C147.814,42.791 148.263,43.24 148.263,43.789 L148.263,43.79 C148.263,44.339 147.814,44.788 147.265,44.788" id="Fill-488" fill="#62A6CF"></path>
177
+ <path d="M135.662,43.541 C135.527,43.541 135.414,43.655 135.414,43.79 C135.414,43.924 135.527,44.038 135.662,44.038 L147.265,44.038 C147.399,44.038 147.514,43.924 147.514,43.79 C147.514,43.655 147.399,43.541 147.265,43.541 L135.662,43.541 Z M147.265,45.538 L135.662,45.538 C134.698,45.538 133.914,44.754 133.914,43.79 C133.914,42.825 134.698,42.041 135.662,42.041 L147.265,42.041 C148.229,42.041 149.014,42.825 149.014,43.79 C149.014,44.754 148.229,45.538 147.265,45.538 L147.265,45.538 Z" id="Fill-489" fill="#0073AA"></path>
178
+ <polyline id="Fill-492" fill="#0073AA" points="137.017 43.04 134.32 34.334 135.754 33.891 138.45 42.596 137.017 43.04"></polyline>
179
+ <polyline id="Fill-493" fill="#0073AA" points="145.721 43.024 144.279 42.611 146.729 34.065 148.17 34.479 145.721 43.024"></polyline>
180
+ <path d="M104.043,65.339 L102.706,65.339 C100.86,65.339 99.349,63.829 99.349,61.982 C99.349,63.829 100.86,65.339 102.706,65.339 L104.043,65.339" id="Fill-494" fill="#96BCD2"></path>
181
+ <path d="M117.288,66.089 L116.041,66.089 L116.041,64.589 L116.179,64.589 L117.288,66.089 M108.372,55.373 C108.205,55.349 108.039,55.337 107.875,55.337 L107.875,53.837 L107.958,53.843 L107.958,55.318 C108.101,55.318 108.24,55.337 108.372,55.373" id="Fill-497" fill="#2871AB"></path>
182
+ <path d="M66.57,62.586 L97.489,105.676 L34.296,105.676 L66.149,62.583 C66.254,62.441 66.467,62.443 66.57,62.586" id="Fill-499" fill="#E7F7FF"></path>
183
+ <path d="M33.692,105.23 L65.546,62.137 C65.735,61.881 66.039,61.727 66.358,61.727 C66.685,61.729 66.989,61.886 67.178,62.147 L77.6536458,76.6425781 L76.6692708,78.1077474 L66.35,63.574 L34.899,106.122 L33.692,105.23 Z" id="Fill-500" fill="#0073AA"></path>
184
+ <polyline id="Fill-501" fill="#96BCD2" points="85.855 89.463 75.321 74.782 85.855 89.463"></polyline>
185
+ <polygon id="Fill-502" fill="#8AC4E6" points="52.948 105.181 75.134847 75.8929036 97.134 105.181"></polygon>
186
+ <polyline id="Fill-504" fill="#ADDDF8" points="143.251 105.181 56.448 105.181 99.205 45.594 143.251 105.181"></polyline>
187
+ <polyline id="Fill-505" fill="#FFFFFF" points="109.61 59.649 107.731 57.282 107.841 57.133 99.193 45.594 91.22 57.133 90.913 57.133 88.916 59.649 92.121 64.116 94.206 61.295 99.407 68.543 104.542 61.596 106.405 64.116 109.61 59.649"></polyline>
188
+ <polyline id="Fill-506" fill="#0073AA" points="142.641188 106.122 98.9618386 46.8885729 56.5677088 106.112927 55.3418229 105.231871 98.9437369 44.32 143.854 105.222798 142.641188 106.122"></polyline>
189
+ <polygon id="Fill-507" fill="#0073AA" points="98.455 47.073 99.955 47.073 99.955 32.087 98.455 32.087"></polygon>
190
+ <polyline id="Fill-508" fill="#8AC4E6" points="115.521 45.057 105.174 45.057 105.174 37.006 115.521 37.006 113.909 41.031 115.521 45.057"></polyline>
191
+ <path d="M105.924,44.307 L114.412,44.307 L113.102,41.031 L114.412,37.756 L105.924,37.756 L105.924,44.307 Z M116.629,45.807 L104.424,45.807 L104.424,36.256 L116.629,36.256 L114.717,41.031 L116.629,45.807 L116.629,45.807 Z" id="Fill-509" fill="#0073AA"></path>
192
+ <polygon id="Fill-510" fill="#ADDDF8" points="99.263 42.37 109.61 42.37 109.61 34.319 99.263 34.319"></polygon>
193
+ <path d="M100.013,41.62 L108.86,41.62 L108.86,35.069 L100.013,35.069 L100.013,41.62 Z M98.513,43.12 L110.36,43.12 L110.36,33.569 L98.513,33.569 L98.513,43.12 Z" id="Fill-511" fill="#0073AA"></path>
194
+ <path d="M21.023,89.476 C12.061,87.325 13.217,80.28 13.229,80.209 L13.341,79.591 L19.459,79.591 L19.459,81.091 L14.659,81.091 C14.643,82.622 15.225,86.542 21.373,88.017 L21.023,89.476" id="Fill-512" fill="#0073AA"></path>
195
+ <path d="M36.928,89.476 L36.578,88.017 C42.766,86.532 43.325,82.62 43.297,81.091 L38.491,81.091 L38.491,79.591 L44.611,79.591 L44.722,80.209 C44.734,80.28 45.89,87.325 36.928,89.476" id="Fill-513" fill="#0073AA"></path>
196
+ <path d="M38.565,81.6647988 C38.565,80.3060295 38.334,79.0116845 37.931,77.813 L19.997,77.813 C19.594,79.0116845 19.363,80.3060295 19.363,81.6647988 C19.363,87.2404387 23.08,92.8209593 27.864,93.5784342 L27.864,101.038 L30.064,101.038 L30.064,93.5784342 C34.848,92.8209593 38.565,87.2404387 38.565,81.6647988" id="Fill-514" fill="#E7F7FF"></path>
197
+ <path d="M28.39,93.958 C23.949,92.768 22.961,87.189 22.961,81.759 C22.961,80.367 23.191,79.041 23.594,77.813 L21.436,77.813 C21.033,79.041 20.802,80.367 20.802,81.759 C20.802,87.471 23.08,93.188 27.864,93.964 C27.864,93.964 28.159,94.02 28.39,93.958" id="Fill-515" fill="#FFFFFF"></path>
198
+ <polygon id="Fill-516" fill="#0073AA" points="24.596 78.563 26.353 78.563 26.353 77.063 24.596 77.063"></polygon>
199
+ <path d="M30.815,100.856 L27.114,100.856 L27.114,93.5373925 C22.372,92.4292537 18.613,87.1764125 18.613,81.480504 C18.613,80.1465984 18.839,78.8239811 19.284,77.5483986 L19.454,77.063 L22.971,77.063 L22.971,78.4740426 L20.553,78.4740426 C20.261,79.4551875 20.113,80.4645533 20.113,81.480504 C20.113,86.6750221 23.718,91.6146118 27.984,92.2655728 L28.614,92.3615237 L28.614,99.4449574 L29.315,99.4449574 L29.315,92.3615237 L29.944,92.2655728 C34.21,91.6146118 37.815,86.6750221 37.815,81.480504 C37.815,80.465494 37.667,79.4561282 37.376,78.4740426 L27.461,78.4740426 L27.461,77.063 L38.476,77.063 L38.645,77.5483986 C39.089,78.8249218 39.315,80.1484798 39.315,81.480504 C39.315,87.1764125 35.556,92.4292537 30.815,93.5373925 L30.815,100.856" id="Fill-517" fill="#0073AA"></path>
200
+ <polygon id="Fill-518" fill="#0073AA" points="26.758 96.12 31.287 96.12 31.287 94.62 26.758 94.62"></polygon>
201
+ <polygon id="Fill-519" fill="#0073AA" points="26.758 98.803 31.287 98.803 31.287 97.303 26.758 97.303"></polygon>
202
+ <path d="M24.061,105.007 L33.771,105.007 L33.771,101.861 C33.771,101.674 33.618,101.522 33.432,101.522 L24.399,101.522 C24.213,101.522 24.061,101.674 24.061,101.861 L24.061,105.007 Z M35.271,106.507 L22.561,106.507 L22.561,101.861 C22.561,100.847 23.386,100.022 24.399,100.022 L33.432,100.022 C34.445,100.022 35.271,100.847 35.271,101.861 L35.271,106.507 L35.271,106.507 Z" id="Fill-521" fill="#0073AA"></path>
203
+ <polygon id="Fill-522" fill="#FFFFFF" points="25.909 104.025 31.923 104.025 31.923 101.625 25.909 101.625"></polygon>
204
+ <polygon id="Fill-523" fill="#0073AA" points="27.461 103.575 30.289 103.575 30.289 102.075 27.461 102.075"></polygon>
205
+ <polygon id="Fill-524" fill="#0073AA" points="21.198 106.507 36.418 106.507 36.418 105.007 21.198 105.007"></polygon>
206
+ <polygon id="Fill-525" fill="#0073AA" points="0.294 106.426 3.033 106.426 3.033 104.926 0.294 104.926"></polygon>
207
+ <polygon id="Fill-526" fill="#0073AA" points="170.241 106.426 176.29 106.426 176.29 104.926 170.241 104.926"></polygon>
208
+ <polygon id="Fill-527" fill="#0073AA" points="4.856 106.426 168.679 106.426 168.679 104.926 4.856 104.926"></polygon>
209
+ <path d="M80.646,41.035 C80.646,45.432 77.082,48.996 72.685,48.996 C68.288,48.996 64.723,45.432 64.723,41.035 C64.723,36.638 68.288,33.073 72.685,33.073 C77.082,33.073 80.646,36.638 80.646,41.035" id="Fill-528" fill="#E7F7FF"></path>
210
+
211
+ <g class="spinning">
212
+ <path d="M72.685,33.823 C68.708,33.823 65.473,37.058 65.473,41.035 C65.473,45.011 68.708,48.246 72.685,48.246 C76.661,48.246 79.896,45.011 79.896,41.035 C79.896,37.058 76.661,33.823 72.685,33.823 Z M72.685,49.746 C67.881,49.746 63.973,45.838 63.973,41.035 C63.973,36.231 67.881,32.323 72.685,32.323 C77.488,32.323 81.396,36.231 81.396,41.035 C81.396,45.838 77.488,49.746 72.685,49.746 L72.685,49.746 Z" id="Fill-529" fill="#0073AA"></path>
213
+ <polygon id="Fill-530" fill="#0073AA" points="71.935 31.075 73.435 31.075 73.435 27.179 71.935 27.179"></polygon>
214
+ <polyline id="Fill-531" fill="#0073AA" points="78.357 32.814 77.063 32.057 78.045 30.375 79.34 31.132 78.357 32.814"></polyline>
215
+ <polyline id="Fill-532" fill="#0073AA" points="81.73 36.799 80.994 35.493 84.388 33.581 85.124 34.887 81.73 36.799"></polyline>
216
+ <polyline id="Fill-533" fill="#0073AA" points="84.567 42.003 82.619 41.942 82.666 40.443 84.614 40.504 84.567 42.003"></polyline>
217
+ <polyline id="Fill-534" fill="#0073AA" points="84.146 48.855 80.815 46.837 81.592 45.554 84.924 47.572 84.146 48.855"></polyline>
218
+ <polyline id="Fill-535" fill="#0073AA" points="77.517 51.807 76.751 50.094 78.12 49.482 78.886 51.194 77.517 51.807"></polyline>
219
+ <polyline id="Fill-536" fill="#0073AA" points="72.996 54.907 71.496 54.859 71.62 50.965 73.12 51.013 72.996 54.907"></polyline>
220
+ <polyline id="Fill-537" fill="#0073AA" points="66.99 51.519 65.719 50.722 66.754 49.072 68.025 49.869 66.99 51.519"></polyline>
221
+ <polyline id="Fill-538" fill="#0073AA" points="60.752 48.115 60.057 46.786 63.509 44.982 64.204 46.311 60.752 48.115"></polyline>
222
+ <polygon id="Fill-539" fill="#0073AA" points="60.79 41.313 62.736 41.313 62.736 39.813 60.79 39.813"></polygon>
223
+ <polyline id="Fill-540" fill="#0073AA" points="63.926 36.236 60.658 34.113 61.475 32.855 64.742 34.978 63.926 36.236"></polyline>
224
+ <polyline id="Fill-541" fill="#0073AA" points="67.543 32.472 66.72 30.835 68.06 30.161 68.883 31.798 67.543 32.472"></polyline>
225
+ <path d="M156.536,105.296 L157.412,100.276 C157.587,99.271 156.814,98.351 155.794,98.351 L138.854,98.351 C137.834,98.351 137.061,99.271 137.236,100.276 L138.112,105.296 L156.536,105.296" id="Fill-542" fill="#FFFFFF"></path>
226
+
227
+ </g>
228
+
229
+ <path d="M157.274,105.425 L155.796,105.167 L156.694,100.023 C156.735,99.79 156.674,99.563 156.523,99.382 C156.37,99.201 156.156,99.102 155.92,99.102 L138.729,99.102 C138.492,99.102 138.278,99.201 138.126,99.382 C137.975,99.563 137.913,99.79 137.954,100.023 L138.852,105.167 L137.373,105.425 L136.476,100.281 C136.359,99.614 136.543,98.935 136.979,98.417 C137.413,97.898 138.052,97.602 138.729,97.602 L155.92,97.602 C156.597,97.602 157.234,97.898 157.67,98.417 C158.105,98.935 158.289,99.614 158.173,100.281 L157.274,105.425" id="Fill-543" fill="#0073AA"></path>
230
+ <polygon id="Fill-544" fill="#0073AA" points="146.314 98.834 148.06 98.834 148.06 93.06 146.314 93.06"></polygon>
231
+ <path d="M157.033,86.407 C157.033,86.407 157.34,90.568 154.964,92.945 C152.587,95.321 148.426,95.014 148.426,95.014 C148.426,95.014 148.118,90.852 150.495,88.475 C152.871,86.099 157.033,86.407 157.033,86.407" id="Fill-550" fill="#E7F7FF"></path>
232
+ <path d="M156.292,87.148 C155.09,87.181 152.584,87.446 151.024,89.006 C149.467,90.564 149.2,93.069 149.168,94.272 C150.37,94.239 152.875,93.973 154.434,92.414 C155.999,90.849 156.262,88.349 156.292,87.148 Z M148.918,95.778 C148.604,95.778 148.406,95.764 148.37,95.762 L147.726,95.714 L147.678,95.069 C147.664,94.885 147.375,90.536 149.964,87.945 C152.555,85.355 156.905,85.646 157.088,85.659 L157.732,85.707 L157.78,86.351 C157.794,86.535 158.084,90.885 155.494,93.475 C153.414,95.555 150.198,95.778 148.918,95.778 L148.918,95.778 Z" id="Fill-551" fill="#0073AA"></path>
233
+ <path d="M146.488,95.014 C146.488,95.014 146.796,90.852 144.419,88.475 C142.042,86.099 137.881,86.407 137.881,86.407 C137.881,86.407 137.573,90.568 139.95,92.945 C142.327,95.321 146.488,95.014 146.488,95.014" id="Fill-552" fill="#E7F7FF"></path>
234
+ <path d="M138.623,87.147 C138.655,88.349 138.921,90.855 140.48,92.414 C142.039,93.973 144.544,94.239 145.746,94.272 C145.714,93.069 145.447,90.564 143.889,89.006 C142.323,87.44 139.823,87.177 138.623,87.147 Z M145.996,95.778 C144.716,95.778 141.5,95.555 139.42,93.475 C136.83,90.885 137.119,86.535 137.133,86.352 L137.181,85.707 L137.825,85.659 C138.009,85.646 142.359,85.354 144.949,87.945 C147.539,90.535 147.25,94.885 147.236,95.069 L147.189,95.714 L146.544,95.762 C146.508,95.764 146.31,95.778 145.996,95.778 L145.996,95.778 Z" id="Fill-553" fill="#0073AA"></path>
235
+ </g>
236
+ </symbol>
237
+
238
+
239
+ <symbol id="icon-plugins" width="179px" height="95px" viewBox="0 0 179 95" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
240
+ <!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
241
+ <desc>Created with Sketch.</desc>
242
+ <defs></defs>
243
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
244
+ <path d="M156.632,94.094 L156.632,20.501 C156.632,18.283 155.034,16.485 153.064,16.485 L100.33,16.485 C98.36,16.485 96.762,18.283 96.762,20.501 L96.762,94.094 L156.632,94.094" id="Fill-183" fill="#FFFFFF"></path>
245
+ <path d="M96.762,19.486 C96.762,17.828 98.105,16.485 99.763,16.485 L153.631,16.485 C155.288,16.485 156.632,17.828 156.632,19.486 L156.632,25.734 L96.762,25.734 L96.762,19.486 Z" id="Fill-184" fill="#FFFFFF"></path>
246
+ <polygon id="Fill-185" fill="#0073AA" points="97.073 26.647 156.632 26.647 156.632 25.147 97.073 25.147"></polygon>
247
+ <polygon id="Fill-186" fill="#0073AA" points="103.638 22.368 105.036 22.368 105.036 20.868 103.638 20.868"></polygon>
248
+ <polygon id="Fill-187" fill="#0073AA" points="107.687 22.368 109.086 22.368 109.086 20.868 107.687 20.868"></polygon>
249
+ <polygon id="Fill-188" fill="#0073AA" points="111.738 22.368 113.136 22.368 113.136 20.868 111.738 20.868"></polygon>
250
+ <polygon id="Fill-189" fill="#E7F7FF" points="104.782 50.091 148.611 50.091 148.611 32.803 104.782 32.803"></polygon>
251
+ <polygon id="Fill-190" fill="#9CC9E1" points="104.787 78.355 118.113 78.355 118.113 61.068 104.787 61.068"></polygon>
252
+ <polygon id="Fill-191" fill="#D1E8F0" points="120.038 78.355 133.364 78.355 133.364 61.068 120.038 61.068"></polygon>
253
+ <path d="M157.382,48.843 L155.882,48.843 L155.882,20.501 C155.882,18.7 154.618,17.235 153.064,17.235 L100.33,17.235 C98.776,17.235 97.512,18.7 97.512,20.501 L97.512,34.594 L96.012,34.594 L96.012,20.501 C96.012,17.873 97.949,15.735 100.33,15.735 L153.064,15.735 C155.445,15.735 157.382,17.873 157.382,20.501 L157.382,48.843" id="Fill-194" fill="#0073AA"></path>
254
+ <polygon id="Fill-195" fill="#0073AA" points="155.882 94.094 157.382 94.094 157.382 68.271 155.882 68.271"></polygon>
255
+ <path d="M81.917,3.637 L77.643,3.637 C77.229,3.637 76.893,3.301 76.893,2.887 C76.893,2.473 77.229,2.137 77.643,2.137 L81.917,2.137 C82.331,2.137 82.667,2.473 82.667,2.887 C82.667,3.301 82.331,3.637 81.917,3.637" id="Fill-196" fill="#0073AA"></path>
256
+ <path d="M79.78,5.774 C79.366,5.774 79.03,5.439 79.03,5.024 L79.03,0.75 C79.03,0.336 79.366,0 79.78,0 C80.194,0 80.53,0.336 80.53,0.75 L80.53,5.024 C80.53,5.439 80.194,5.774 79.78,5.774" id="Fill-197" fill="#0073AA"></path>
257
+ <path d="M8.165,57.95 L3.891,57.95 C3.477,57.95 3.141,57.614 3.141,57.2 C3.141,56.786 3.477,56.45 3.891,56.45 L8.165,56.45 C8.579,56.45 8.915,56.786 8.915,57.2 C8.915,57.614 8.579,57.95 8.165,57.95" id="Fill-198" fill="#0073AA"></path>
258
+ <path d="M6.028,60.087 C5.614,60.087 5.278,59.751 5.278,59.337 L5.278,55.063 C5.278,54.648 5.614,54.313 6.028,54.313 C6.442,54.313 6.778,54.648 6.778,55.063 L6.778,59.337 C6.778,59.751 6.442,60.087 6.028,60.087" id="Fill-199" fill="#0073AA"></path>
259
+ <path d="M167.204,14.301 C167.013,14.301 166.821,14.228 166.674,14.081 C166.381,13.788 166.381,13.314 166.674,13.021 L169.696,9.999 C169.989,9.706 170.464,9.706 170.757,9.999 C171.05,10.292 171.05,10.767 170.757,11.06 L167.735,14.081 C167.588,14.228 167.396,14.301 167.204,14.301" id="Fill-200" fill="#0073AA"></path>
260
+ <path d="M170.226,14.301 C170.035,14.301 169.843,14.228 169.696,14.081 L166.674,11.06 C166.381,10.767 166.381,10.292 166.674,9.999 C166.967,9.706 167.442,9.706 167.735,9.999 L170.757,13.021 C171.05,13.314 171.05,13.788 170.757,14.081 C170.61,14.228 170.418,14.301 170.226,14.301" id="Fill-201" fill="#0073AA"></path>
261
+ <polygon id="Fill-204" fill="#0073AA" points="21.566 3.637 72.286 3.637 72.286 2.137 21.566 2.137"></polygon>
262
+ <polygon id="Fill-206" fill="#0073AA" points="0 40.018 25.157 40.018 25.157 38.518 0 38.518"></polygon>
263
+ <path d="M8.698,32.416 C8.698,33.16 8.095,33.763 7.351,33.763 C6.608,33.763 6.005,33.16 6.005,32.416 C6.005,31.672 6.608,31.069 7.351,31.069 C8.095,31.069 8.698,31.672 8.698,32.416" id="Fill-210" fill="#0073AA"></path>
264
+ <path d="M117.815,2.887 C117.815,3.631 117.212,4.234 116.468,4.234 C115.725,4.234 115.122,3.631 115.122,2.887 C115.122,2.143 115.725,1.54 116.468,1.54 C117.212,1.54 117.815,2.143 117.815,2.887" id="Fill-211" fill="#0073AA"></path>
265
+ <path d="M172.52,56.45 C172.52,57.194 171.917,57.797 171.173,57.797 C170.43,57.797 169.827,57.194 169.827,56.45 C169.827,55.707 170.43,55.104 171.173,55.104 C171.917,55.104 172.52,55.707 172.52,56.45" id="Fill-212" fill="#0073AA"></path>
266
+ <path d="M38.479,94.094 L34.696,94.094 C34.411,94.094 34.18,93.863 34.18,93.578 L34.18,39.087 L34.227,38.871 L36.119,34.773 C36.303,34.373 36.872,34.373 37.056,34.773 L38.947,38.871 L38.995,39.087 L38.995,93.578 C38.995,93.863 38.764,94.094 38.479,94.094" id="Fill-214" fill="#FFFFFF"></path>
267
+ <polygon id="Fill-215" fill="#ADDDF8" points="34.18 88.878 38.995 88.878 38.995 40.826 33.4855957 40.826"></polygon>
268
+ <path d="M38.245,94.094 L38.245,39.139 L36.588,35.548 L34.93,39.139 L34.93,58.3605957 L33.43,58.3605957 L33.43,38.809 L35.627,34.048 C35.802,33.669 36.17,33.434 36.588,33.434 C37.005,33.434 37.373,33.669 37.548,34.048 L39.745,38.809 L39.745,94.094 L38.245,94.094 Z" id="Fill-216" fill="#0073AA"></path>
269
+ <polygon id="Fill-217" fill="#0073AA" points="33.43 93.919 34.93 93.919 34.93 74.236 33.43 74.236"></polygon>
270
+ <polygon id="Fill-218" fill="#0073AA" points="34.014 41.575 38.995 41.575 38.995 40.075 34.014 40.075"></polygon>
271
+ <path d="M47.926,94.182 L47.926,22 L42.8954316,22 C42.5364316,22 42.2454316,22.325 42.2454316,22.728 L42.2454316,93.454 C42.2454316,93.856 42.5364316,94.182 42.8954316,94.182 L47.926,94.182 Z" id="Fill-220" fill="#E7F7FF"></path>
272
+ <path d="M43.451,93.302 L41.951,93.302 L41.951,22.703 C41.951,21.853 42.643,21.161 43.493,21.161 L53.255,21.161 C54.105,21.161 54.797,21.853 54.797,22.703 L54.797,34.302 L53.297,34.302 L53.297,22.703 L43.493,22.661 L43.451,93.302" id="Fill-221" fill="#0073AA"></path>
273
+ <polygon id="Fill-223" fill="#0073AA" points="42.701 81.363 47.926 81.363 47.926 79.863 42.701 79.863"></polygon>
274
+ <polygon id="Fill-224" fill="#0073AA" points="42.701 73.772 47.926 73.772 47.926 72.272 42.701 72.272"></polygon>
275
+ <polygon id="Fill-225" fill="#0073AA" points="42.701 66.182 47.926 66.182 47.926 64.682 42.701 64.682"></polygon>
276
+ <polygon id="Fill-226" fill="#0073AA" points="42.701 58.591 47.926 58.591 47.926 57.091 42.701 57.091"></polygon>
277
+ <polygon id="Fill-227" fill="#0073AA" points="42.701 51.001 47.926 51.001 47.926 49.501 42.701 49.501"></polygon>
278
+ <polygon id="Fill-228" fill="#0073AA" points="42.701 43.41 47.926 43.41 47.926 41.91 42.701 41.91"></polygon>
279
+ <polygon id="Fill-229" fill="#0073AA" points="42.701 35.819 47.926 35.819 47.926 34.319 42.701 34.319"></polygon>
280
+ <polygon id="Fill-230" fill="#0073AA" points="42.701 28.34 47.926 28.34 47.926 26.84 42.701 26.84"></polygon>
281
+ <path d="M123.044,91.052 L48.836,91.052 C48.286,91.052 47.836,90.602 47.836,90.052 L47.836,37.275 C47.836,36.725 48.286,36.275 48.836,36.275 L123.044,36.275 C123.594,36.275 124.044,36.725 124.044,37.275 L124.044,90.052 C124.044,90.602 123.594,91.052 123.044,91.052" id="Fill-231" fill="#8AC4E6"></path>
282
+ <polygon id="Fill-232" fill="#0073AA" points="102.77 37.025 108.033 37.025 108.033 35.525 102.77 35.525"></polygon>
283
+ <path d="M122.151,91.803 L49.73,91.803 C48.272,91.803 47.086,90.617 47.086,89.159 L47.086,38.169 C47.086,36.711 48.272,35.525 49.73,35.525 L101.397,35.525 L101.397,37.025 L49.73,37.025 C49.099,37.025 48.586,37.538 48.586,38.169 L48.586,89.159 C48.586,89.79 49.099,90.303 49.73,90.303 L122.151,90.303 C122.781,90.303 123.294,89.79 123.294,89.159 L124.794,89.159 C124.794,90.617 123.608,91.803 122.151,91.803" id="Fill-233" fill="#0073AA"></path>
284
+ <polygon id="Fill-234" fill="#0073AA" points="109.752 37.025 119.93 37.025 119.93 35.525 109.752 35.525"></polygon>
285
+ <polygon id="Fill-235" fill="#FFFFFF" points="51.537 85.036 119.93 85.036 119.93 40.585 51.537 40.585"></polygon>
286
+ <path d="M122.712,94.094 L48.328,94.094 C46.542,94.094 45.019,92.8 44.73,91.038 L44.2,87.806 L78.676,87.806 L79.995,89.7 L89.522,89.7 L90.948,87.806 L127.267,87.806 L126.806,90.617 C126.477,92.622 124.744,94.094 122.712,94.094" id="Fill-236" fill="#FFFFFF"></path>
287
+ <path d="M45.083,88.557 L45.47,90.917 C45.701,92.323 46.903,93.344 48.328,93.344 L122.712,93.344 C124.385,93.344 125.795,92.147 126.065,90.496 L126.384,88.557 L91.322,88.557 L89.895,90.45 L79.604,90.45 L78.285,88.557 L45.083,88.557 M122.712,94.844 L48.328,94.844 C46.164,94.844 44.34,93.294 43.99,91.159 L43.317,87.057 L79.068,87.057 L80.387,88.95 L89.147,88.95 L90.574,87.057 L128.149,87.057 L127.546,90.738 C127.156,93.117 125.123,94.844 122.712,94.844" id="Fill-237" fill="#0073AA"></path>
288
+ <polyline id="Fill-239" fill="#B4D7E7" points="128.149 87.057 124.044 87.057 124.044 78.355 124.044 87.056 128.149 87.056 128.149 87.057"></polyline>
289
+ <polyline id="Fill-241" fill="#0073AA" points="119.93 37.025 119.627 37.025 119.144 35.525 119.93 35.525 119.93 37.025"></polyline>
290
+ <polygon id="Fill-242" fill="#B4D7E7" points="119.821 85.036 119.93 85.036 119.93 45.915 119.821 45.915"></polygon>
291
+ <path d="M125.308,94.094 L119.821,94.094 L119.821,93.344 L122.712,93.344 C124.385,93.344 125.795,92.147 126.065,90.496 L126.384,88.557 L119.821,88.557 L119.821,87.057 L128.149,87.057 L127.546,90.738 C127.31,92.177 126.467,93.37 125.308,94.094" id="Fill-243" fill="#0073AA"></path>
292
+ <path d="M128.529,47.577 L128.569,47.583 L131.29,47.591 L135.421,35.076 L129.776,25.121 L128.894,25.121 L128.811,34.648 C129.013,34.738 129.735,35.046 129.899,35.209 C130.612,35.919 130.614,37.073 129.905,37.785 C129.196,38.496 128.041,38.499 127.329,37.788 C126.616,37.079 126.615,35.925 127.324,35.213 C127.478,35.06 128.022,34.709 128.208,34.623 L128.428,25.121 L127.56,25.121 L121.77,35.023 L125.808,47.568 L128.529,47.577" id="Fill-244" fill="#FFFFFF"></path>
293
+ <path d="M128.484,35.324 C128.323,35.411 127.943,35.66 127.848,35.749 C127.653,35.944 127.542,36.214 127.543,36.5 C127.543,36.786 127.655,37.055 127.857,37.257 C128.06,37.459 128.329,37.57 128.615,37.57 L128.617,37.57 C128.903,37.569 129.172,37.458 129.373,37.256 C129.789,36.837 129.788,36.157 129.369,35.74 C129.348,35.729 129.171,35.624 128.564,35.358 L128.484,35.324 Z M128.625,46.833 L130.748,46.84 L134.605,35.157 L129.633,26.388 L129.566,34.165 C130.047,34.39 130.282,34.532 130.428,34.677 C131.432,35.678 131.436,37.309 130.436,38.314 C129.951,38.801 129.306,39.068 128.619,39.07 L128.615,39.07 C127.929,39.07 127.285,38.804 126.799,38.319 C126.313,37.835 126.044,37.19 126.043,36.503 C126.042,35.816 126.308,35.17 126.792,34.684 C126.936,34.541 127.217,34.345 127.468,34.186 L127.647,26.458 L122.587,35.111 L126.354,46.82 L128.625,46.833 Z M131.831,48.344 L125.261,48.316 L120.954,34.934 L127.13,24.371 L130.213,24.371 L136.238,34.995 L131.831,48.344 L131.831,48.344 Z" id="Fill-245" fill="#0073AA"></path>
294
+ <polygon id="Fill-246" fill="#E7F7FF" points="122.571 94.094 134.621 94.094 134.621 46.665 122.571 46.665"></polygon>
295
+ <polygon id="Fill-247" fill="#8AC4E6" points="128.596 94.094 134.467 94.094 134.467 46.665 128.596 46.665"></polygon>
296
+ <polygon id="Fill-248" fill="#0073AA" points="127.846 34.623 129.346 34.623 129.346 25.121 127.846 25.121"></polygon>
297
+ <path d="M128.596,35.222 C127.836,35.222 127.219,35.84 127.219,36.599 C127.219,37.358 127.836,37.976 128.596,37.976 C129.355,37.976 129.972,37.358 129.972,36.599 C129.972,35.84 129.355,35.222 128.596,35.222 Z M128.596,39.476 C127.009,39.476 125.719,38.185 125.719,36.599 C125.719,35.013 127.009,33.722 128.596,33.722 C130.182,33.722 131.472,35.013 131.472,36.599 C131.472,38.185 130.182,39.476 128.596,39.476 L128.596,39.476 Z" id="Fill-249" fill="#0073AA"></path>
298
+ <polygon id="Fill-250" fill="#0073AA" points="133.87 94.094 135.37 94.094 135.37 79.333 133.87 79.333"></polygon>
299
+ <polyline id="Fill-251" fill="#0073AA" points="123.321 94.094 121.821 94.094 121.821 45.915 135.37 45.915 135.37 49.952 133.87 49.952 133.87 47.415 123.321 47.415 123.321 94.094"></polyline>
300
+ <path d="M147.043,49.952 L150.585,49.952 L161.415,52.531 C162.41,52.768 163.112,53.657 163.112,54.679 L163.112,55.356 L147.043,55.356 L147.043,49.952" id="Fill-252" fill="#E7F7FF"></path>
301
+ <path d="M147.043,67.248 L150.585,67.248 L161.415,64.668 C162.41,64.432 163.112,63.543 163.112,62.521 L163.112,61.843 L147.043,61.843 L147.043,67.248" id="Fill-253" fill="#E7F7FF"></path>
302
+ <path d="M147.793,54.607 L162.36,54.607 C162.328,53.96 161.877,53.412 161.241,53.261 L150.497,50.702 L147.793,50.702 L147.793,54.607 Z M163.862,56.107 L146.293,56.107 L146.293,49.202 L150.759,49.223 L161.589,51.802 C162.927,52.121 163.862,53.304 163.862,54.679 L163.862,56.107 L163.862,56.107 Z" id="Fill-254" fill="#0073AA"></path>
303
+ <path d="M147.793,66.498 L150.497,66.498 L161.242,63.939 C161.878,63.787 162.328,63.239 162.36,62.593 L147.793,62.593 L147.793,66.498 Z M150.673,67.998 L146.293,67.998 L146.293,61.093 L163.862,61.093 L163.862,62.521 C163.862,63.897 162.927,65.079 161.589,65.397 L150.673,67.998 L150.673,67.998 Z" id="Fill-255" fill="#0073AA"></path>
304
+ <path d="M138.74,60.795 L136.212,60.795 L128.485,62.636 C127.776,62.805 127.275,63.439 127.275,64.168 L127.275,64.651 L138.74,64.651 L138.74,60.795" id="Fill-256" fill="#ADDDF8"></path>
305
+ <path d="M138.74,55.514 L136.212,55.514 L128.485,53.674 C127.776,53.505 127.275,52.871 127.275,52.141 L127.275,51.658 L138.74,51.658 L138.74,55.514" id="Fill-257" fill="#ADDDF8"></path>
306
+ <path d="M128.069,63.901 L137.99,63.901 L137.99,61.545 L136.3,61.545 L128.659,63.365 C128.376,63.433 128.158,63.639 128.069,63.901 Z M139.49,65.401 L126.525,65.401 L126.525,64.168 C126.525,63.087 127.26,62.156 128.311,61.906 L136.212,60.045 L139.49,60.045 L139.49,65.401 L139.49,65.401 Z" id="Fill-258" fill="#0073AA"></path>
307
+ <path d="M136.3,54.764 L137.99,54.764 L137.99,52.408 L128.069,52.408 C128.158,52.671 128.376,52.877 128.659,52.944 L136.3,54.764 Z M139.49,56.264 L136.038,56.243 L128.311,54.403 C127.26,54.153 126.525,53.223 126.525,52.142 L126.525,50.908 L139.49,50.908 L139.49,56.264 L139.49,56.264 Z" id="Fill-259" fill="#0073AA"></path>
308
+ <polygon id="Fill-260" fill="#FFFFFF" points="138.188 94.094 147.043 94.094 147.043 49.952 138.188 49.952"></polygon>
309
+ <polyline id="Fill-261" fill="#0073AA" points="147.793 94.094 146.293 94.094 146.293 50.702 138.938 50.702 138.938 94.094 137.438 94.094 137.438 49.202 147.793 49.202 147.793 94.094"></polyline>
310
+ <polygon id="Fill-262" fill="#0073AA" points="138.188 55.509 142.201 55.509 142.201 54.009 138.188 54.009"></polygon>
311
+ <polygon id="Fill-263" fill="#0073AA" points="138.188 59.226 142.201 59.226 142.201 57.726 138.188 57.726"></polygon>
312
+ <polygon id="Fill-264" fill="#0073AA" points="138.188 62.941 142.201 62.941 142.201 61.441 138.188 61.441"></polygon>
313
+ <polygon id="Fill-265" fill="#0073AA" points="138.188 85.237 142.201 85.237 142.201 83.737 138.188 83.737"></polygon>
314
+ <polygon id="Fill-266" fill="#0073AA" points="138.188 88.953 142.201 88.953 142.201 87.453 138.188 87.453"></polygon>
315
+ <polygon id="Fill-267" fill="#0073AA" points="138.188 92.669 142.201 92.669 142.201 91.169 138.188 91.169"></polygon>
316
+ <polygon id="Fill-268" fill="#8AC4E6" points="136.16 82.149 147.558 82.149 147.558 67.346 136.16 67.346"></polygon>
317
+ <path d="M136.91,81.398 L146.808,81.398 L146.808,68.096 L136.91,68.096 L136.91,81.398 Z M135.41,82.898 L148.308,82.898 L148.308,66.596 L135.41,66.596 L135.41,82.898 Z" id="Fill-269" fill="#0073AA"></path>
318
+ <polygon id="Fill-270" fill="#D1E8F0" points="132.496 77.619 135.003 77.619 135.003 71.657 132.496 71.657"></polygon>
319
+ <path d="M133.246,76.869 L134.253,76.869 L134.253,72.406 L133.246,72.406 L133.246,76.869 Z M131.746,78.369 L135.753,78.369 L135.753,70.906 L131.746,70.906 L131.746,78.369 Z" id="Fill-271" fill="#0073AA"></path>
320
+ <path d="M160.608,57.2 C159.905,57.2 159.333,57.772 159.333,58.476 C159.333,59.179 159.905,59.75 160.608,59.75 C161.311,59.75 161.883,59.179 161.883,58.476 C161.883,57.772 161.311,57.2 160.608,57.2 Z M160.608,61.25 C159.077,61.25 157.833,60.006 157.833,58.476 C157.833,56.945 159.077,55.7 160.608,55.7 C162.138,55.7 163.383,56.945 163.383,58.476 C163.383,60.006 162.138,61.25 160.608,61.25 L160.608,61.25 Z" id="Fill-272" fill="#0073AA"></path>
321
+ <path d="M25.87,94.094 L24.269,94.094 C23.719,94.094 23.269,93.644 23.269,93.094 L23.269,89.579 C23.269,89.029 23.719,88.579 24.269,88.579 L25.87,88.579 C26.42,88.579 26.87,89.029 26.87,89.579 L26.87,93.094 C26.87,93.644 26.42,94.094 25.87,94.094" id="Fill-273" fill="#62A6CF"></path>
322
+ <path d="M24.269,89.329 C24.133,89.329 24.019,89.443 24.019,89.579 L24.019,93.094 C24.019,93.229 24.133,93.344 24.269,93.344 L25.87,93.344 C26.006,93.344 26.12,93.229 26.12,93.094 L26.12,89.579 C26.12,89.443 26.006,89.329 25.87,89.329 L24.269,89.329 Z M25.87,94.844 L24.269,94.844 C23.304,94.844 22.519,94.059 22.519,93.094 L22.519,89.579 C22.519,88.614 23.304,87.829 24.269,87.829 L25.87,87.829 C26.835,87.829 27.62,88.614 27.62,89.579 L27.62,93.094 C27.62,94.059 26.835,94.844 25.87,94.844 L25.87,94.844 Z" id="Fill-274" fill="#0073AA"></path>
323
+ <path d="M37.189,66.18 C37.189,59.535 31.802,54.147 25.157,54.147 C18.511,54.147 13.125,59.535 13.125,66.18 C13.125,70.638 15.556,74.522 19.159,76.6 L19.159,88.464 L20.865,91.696 L29.13,91.696 L31.154,88.464 L31.154,76.6 C34.758,74.522 37.189,70.638 37.189,66.18" id="Fill-275" fill="#FFFFFF"></path>
324
+ <polyline id="Fill-276" fill="#ADDDF8" points="19.159 79.913 19.159 89.173 20.865 91.696 29.13 91.696 31.154 89.173 31.154 79.913 19.159 79.913"></polyline>
325
+ <polyline id="Fill-277" fill="#62A6CF" points="31.154 88.464 29.13 91.696 20.865 91.696 19.159 88.464 31.154 88.464"></polyline>
326
+ <path d="M29.546,92.446 L20.412,92.446 L18.41,88.649 L18.41,77.025 C14.676,74.697 12.375,70.581 12.375,66.18 C12.375,63.169 13.442,60.245 15.379,57.946 L16.526,58.913 C14.816,60.941 13.875,63.522 13.875,66.18 C13.875,70.192 16.043,73.937 19.535,75.951 L19.91,76.167 L19.91,88.278 L21.317,90.946 L28.715,90.946 L30.404,88.248 L30.404,76.167 L30.779,75.951 C34.27,73.937 36.439,70.192 36.439,66.18 C36.439,59.959 31.378,54.897 25.157,54.897 C23.691,54.897 22.265,55.174 20.92,55.72 L20.356,54.329 C21.882,53.711 23.497,53.397 25.157,53.397 C32.205,53.397 37.939,59.132 37.939,66.18 C37.939,70.581 35.638,74.697 31.904,77.025 L31.904,88.68 L29.546,92.446" id="Fill-278" fill="#0073AA"></path>
327
+ <path d="M17.682,57.728 L16.617,56.673 C17.379,55.902 17.772,55.677 18.556,55.228 L18.784,55.098 L19.533,56.397 L19.301,56.53 C18.554,56.958 18.302,57.103 17.682,57.728" id="Fill-279" fill="#0073AA"></path>
328
+ <polygon id="Fill-280" fill="#0073AA" points="17.35 81.029 32.964 81.029 32.964 79.529 17.35 79.529"></polygon>
329
+ <polygon id="Fill-281" fill="#0073AA" points="17.35 83.758 32.964 83.758 32.964 82.258 17.35 82.258"></polygon>
330
+ <polygon id="Fill-282" fill="#0073AA" points="17.35 86.485 32.964 86.485 32.964 84.985 17.35 84.985"></polygon>
331
+ <polygon id="Fill-283" fill="#0073AA" points="17.35 89.214 32.964 89.214 32.964 87.714 17.35 87.714"></polygon>
332
+ <path d="M31.488,66.188 C31.488,69.684 28.653,72.519 25.157,72.519 C21.66,72.519 18.826,69.684 18.826,66.188 C18.826,62.691 21.66,59.856 25.157,59.856 C28.653,59.856 31.488,62.691 31.488,66.188" id="Fill-284" fill="#E7F7FF"></path>
333
+ <polygon id="Fill-320" fill="#0073AA" points="7.351 94.844 171.173 94.844 171.173 93.344 7.351 93.344"></polygon>
334
+ <polygon id="Fill-321" fill="#0073AA" points="2.789 94.844 5.528 94.844 5.528 93.344 2.789 93.344"></polygon>
335
+ <polygon id="Fill-322" fill="#0073AA" points="172.736 94.844 178.785 94.844 178.785 93.344 172.736 93.344"></polygon>
336
+ </g>
337
+ </symbol>
338
+
339
+
340
+ <symbol id="icon-license" width="168px" height="104px" viewBox="0 0 168 104" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
341
+ <defs></defs>
342
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
343
+ <path d="M159.557,102.076 L159.557,23.847 C159.557,21.722 157.834,20 155.71,20 L98.847,20 C96.722,20 95,21.722 95,23.847 L95,102.077 L159.557,102.076" id="Fill-16" fill="#ADDDF8"></path>
344
+ <path d="M153.735,103.076 L153.735,24.847 C153.735,22.722 152.274,21 150.473,21 L102.262,21 C100.46,21 99,22.722 99,24.847 L99,103.077 L153.735,103.076" id="Fill-17" fill="#FFFFFF"></path>
345
+ <path d="M95,29.973 L95,23.235 C95,21.448 96.448,20 98.235,20 L156.322,20 C158.108,20 159.557,21.448 159.557,23.235 L159.557,29.973 L95,29.973" id="Fill-18" fill="#ADDDF8"></path>
346
+ <path d="M95.5,33.4394531 L94,33.4394531 L94,22.986 C94,20.788 95.787,19 97.985,19 L106.557,19 L106.557,20.5 L97.985,20.5 C96.614,20.5 95.5,21.615 95.5,22.986 L95.5,30.986 L95.5,33.4394531 Z" id="Fill-19" fill="#0073AA"></path>
347
+ <path d="M160.284,102.85612 L158.784,101.859 L158.784,22.986 C158.784,21.615 157.669,20.5 156.299,20.5 L118,20.5 L118,19 L156.299,19 C158.496,19 160.284,20.788 160.284,22.986 L160.284,101.859 L160.284,102.85612 Z" id="Fill-20" fill="#0073AA"></path>
348
+ <polygon id="Fill-21" fill="#0073AA" points="110 20.5 114.435 20.5 114.435 19 110 19"></polygon>
349
+ <polygon id="Fill-22" fill="#0073AA" points="95 30.5 159.557 30.5 159.557 29 95 29"></polygon>
350
+ <polygon id="Fill-23" fill="#0073AA" points="102 25.5 103.502 25.5 103.502 24 102 24"></polygon>
351
+ <polygon id="Fill-24" fill="#0073AA" points="107 25.5 108.503 25.5 108.503 24 107 24"></polygon>
352
+ <polygon id="Fill-25" fill="#0073AA" points="111 25.5 112.503 25.5 112.503 24 111 24"></polygon>
353
+ <polygon id="Fill-26" fill="#E7F7FF" points="103 55.642 150.26 55.642 150.26 37 103 37"></polygon>
354
+ <polygon id="Fill-27" fill="#D1E8F0" points="103 86.641 117.37 86.641 117.37 68 103 68"></polygon>
355
+ <polygon id="Fill-28" fill="#E7F7FF" points="120 79.641 134.782 79.641 134.782 61 120 61"></polygon>
356
+ <polygon id="Fill-29" fill="#E7F7FF" points="136 79.641 150.369 79.641 150.369 61 136 61"></polygon>
357
+ <polygon id="Fill-28-Copy" fill="#E7F7FF" points="102.48 103.641 134.782 103.641 134.782 84.999 102.48 84.999"></polygon>
358
+ <polygon id="Fill-29-Copy" fill="#E7F7FF" points="136 103.641 150.369 103.641 150.369 85 136 85"></polygon>
359
+ <path d="M167.024,7.5 L162.75,7.5 C162.335,7.5 162,7.164 162,6.75 C162,6.335 162.335,6 162.75,6 L167.024,6 C167.438,6 167.774,6.335 167.774,6.75 C167.774,7.164 167.438,7.5 167.024,7.5" id="Fill-30" fill="#0073AA"></path>
360
+ <path d="M164.75,9.774 C164.336,9.774 164,9.438 164,9.024 L164,4.75 C164,4.336 164.336,4 164.75,4 C165.164,4 165.5,4.336 165.5,4.75 L165.5,9.024 C165.5,9.438 165.164,9.774 164.75,9.774" id="Fill-31" fill="#0073AA"></path>
361
+ <path d="M38.024,3.5 L33.75,3.5 C33.336,3.5 33,3.164 33,2.75 C33,2.336 33.336,2 33.75,2 L38.024,2 C38.438,2 38.774,2.336 38.774,2.75 C38.774,3.164 38.438,3.5 38.024,3.5" id="Fill-32" fill="#0073AA"></path>
362
+ <path d="M35.75,5.774 C35.336,5.774 35,5.438 35,5.024 L35,0.75 C35,0.336 35.336,0 35.75,0 C36.164,0 36.5,0.336 36.5,0.75 L36.5,5.024 C36.5,5.438 36.164,5.774 35.75,5.774" id="Fill-33" fill="#0073AA"></path>
363
+ <polygon id="Fill-42" fill="#0073AA" points="51 3.5 68.395 3.5 68.395 2 51 2"></polygon>
364
+ <polygon id="Fill-43" fill="#0073AA" points="45 3.5 48.57 3.5 48.57 2 45 2"></polygon>
365
+ <path d="M10.553,75.276 C10.553,75.981 9.982,76.553 9.277,76.553 C8.572,76.553 8,75.981 8,75.276 C8,74.571 8.572,74 9.277,74 C9.982,74 10.553,74.571 10.553,75.276" id="Fill-44" fill="#0073AA"></path>
366
+ <path d="M81.83,19.915 C81.83,20.421 81.421,20.83 80.915,20.83 C80.41,20.83 80,20.421 80,19.915 C80,19.409 80.41,19 80.915,19 C81.421,19 81.83,19.409 81.83,19.915" id="Fill-45" fill="#0073AA"></path>
367
+ <path d="M20.444,7.222 C20.444,7.897 19.897,8.444 19.222,8.444 C18.547,8.444 18,7.897 18,7.222 C18,6.547 18.547,6 19.222,6 C19.897,6 20.444,6.547 20.444,7.222" id="Fill-48" fill="#0073AA"></path>
368
+ <path d="M166.444,43.222 C166.444,43.896 165.897,44.443 165.222,44.443 C164.547,44.443 164,43.896 164,43.222 C164,42.547 164.547,42 165.222,42 C165.897,42 166.444,42.547 166.444,43.222" id="Fill-49" fill="#0073AA"></path>
369
+ <polyline id="Fill-50" fill="#A6CFE4" points="100.512 102 95 102 100.512 102"></polyline>
370
+ <path d="M94.8495,88 L98.8495,88 L95.9598297,88 L95.9598297,102.09423 L99.023112,102.09423 L99.023112,103 L94.8495,103 L94.8495,88 Z M94.8495,32.75 L99.3995,32.75 L99.3995,32 L94.8495,32 L94.8495,32.75 Z" id="Fill-51" fill="#3378B0"></path>
371
+ <path d="M41,79.577 L41,34.024801 C41,32.9360152 41.884,32.053 42.974,32.053 L117.087891,32.053 C118.177891,32.053 120.988,34.9388835 120.988,36.0276693 L120.988,79.577 L41,79.577 Z" id="Fill-55" fill="#8AC4E6"></path>
372
+ <polygon id="Fill-56" fill="#0073AA" points="41 58.71 42.5 58.71 42.5 50 41 50"></polygon>
373
+ <path d="M121.774,89.5021973 L120.274,89.5021973 L120.274,36.617 C120.274,34.898 118.875,33.5 117.156,33.5 L58,33.5 L58,32 L117.156,32 C119.702,32 121.774,34.071 121.774,36.617 L121.774,89.5021973 L121.34668,85.7150879" id="Fill-57" fill="#0073AA"></path>
374
+ <polygon id="Fill-58" fill="#FFFFFF" points="46 83.024 117.396 83.024 117.396 37 46 37"></polygon>
375
+ <polygon id="Fill-59" fill="#E7F7FF" points="69 101.266 93.099 101.266 93.099 87 69 87"></polygon>
376
+ <polygon id="Fill-60" fill="#8AC4E6" points="69 93.133 93.099 93.133 93.099 86 69 86"></polygon>
377
+ <path d="M69.5,101.266 L92.099,101.266 L92.099,88.5 L69.5,88.5 L69.5,101.266 Z M68,102.766 L93.599,102.766 L93.599,87 L68,87 L68,102.766 Z" id="Fill-61" fill="#0073AA"></path>
378
+ <path d="M120.988,82.317 L41,82.317 L41,87.6012964 C41,88.2646852 41.896,88.802 43,88.802 L118.989,88.802 C120.093,88.802 120.988,88.2646852 120.988,87.6012964 L120.988,82.317" id="Fill-62" fill="#FFFFFF"></path>
379
+ <polygon id="Fill-63" fill="#0073AA" points="53 89.5 115.489 89.5 115.489 88 53 88"></polygon>
380
+ <polygon id="Fill-64" fill="#0073AA" points="51.5465 89.5 121.3405 89.5 121.3405 78.709 51.5465 78.709"></polygon>
381
+ <polygon id="Fill-66" fill="#FFFFFE" points="136 82.189 138.052 82.189 138.052 68.082 136 68.082"></polygon>
382
+ <path d="M137.746,101.42 L136.566,101.42 C135.149,101.42 134,100.271 134,98.854 L134,82 L140.312,82 L140.312,98.854 C140.312,100.271 139.163,101.42 137.746,101.42" id="Fill-67" fill="#FFFFFF"></path>
383
+ <path d="M134.5,83.5 L134.5,99.604 C134.5,100.606 135.315,101.421 136.316,101.421 L137.495,101.421 C138.497,101.421 139.312,100.606 139.312,99.605 L139.312,83.5 L134.5,83.5 Z M137.495,102.921 L136.316,102.921 C134.488,102.921 133,101.434 133,99.604 L133,82 L140.812,82 L140.812,99.605 C140.812,101.434 139.325,102.921 137.495,102.921 L137.495,102.921 Z" id="Fill-68" fill="#0073AA"></path>
384
+ <polygon id="Fill-69" fill="#0073AA" points="132 83.5 142.066 83.5 142.066 82 132 82"></polygon>
385
+ <polyline id="Fill-70" fill="#FFFFFF" points="138.504 64.191 135 64.191 135.416 58 138.107 58 138.504 64.191"></polyline>
386
+ <path d="M136.283,81.086 L136.835,81.086 L136.835,63.191 L137.506,63.191 L137.205,58.5 L135.919,58.5 L135.604,63.191 L136.283,63.191 L136.283,81.086 Z M138.335,82.586 L134.783,82.586 L134.783,64.691 L134,64.691 L134.517,57 L138.612,57 L139.106,64.691 L138.335,64.691 L138.335,82.586 L138.335,82.586 Z" id="Fill-71" fill="#0073AA"></path>
387
+ <path d="M158.556,66.414 C158.556,63.16 156.675,60.353 153.944,59 L153.944,66.158 L150.278,68.274 L146.612,66.158 L146.612,59 C143.881,60.353 142,63.16 142,66.414 C142,69.82 144.057,72.742 146.996,74.013 L147.043,98.072 C147.043,99.885 148.513,101.354 150.325,101.354 C152.138,101.354 153.607,99.885 153.607,98.072 L153.56,74.013 C156.499,72.742 158.556,69.82 158.556,66.414" id="Fill-72" fill="#FFFFFF"></path>
388
+ <path d="M145.612,60.532 C143.666,61.934 142.5,64.172 142.5,66.622 C142.5,69.626 144.283,72.339 147.044,73.533 L147.495,73.728 L147.543,98.279 C147.543,99.677 148.679,100.812 150.075,100.812 C151.472,100.812 152.608,99.677 152.608,98.281 L152.559,73.729 L153.012,73.533 C155.772,72.339 157.556,69.626 157.556,66.622 C157.556,64.171 156.389,61.933 154.444,60.532 L154.444,66.799 L150.028,69.349 L145.612,66.799 L145.612,60.532 Z M150.075,102.312 C147.852,102.312 146.043,100.504 146.043,98.281 L145.997,74.7 C142.945,73.177 141,70.058 141,66.622 C141,63.172 142.927,60.074 146.029,58.536 L147.112,58 L147.112,65.932 L150.028,67.616 L152.944,65.932 L152.944,58 L154.027,58.536 C157.129,60.073 159.056,63.171 159.056,66.622 C159.056,70.057 157.111,73.175 154.061,74.699 L154.108,98.279 C154.108,100.504 152.298,102.312 150.075,102.312 L150.075,102.312 Z" id="Fill-73" fill="#0073AA"></path>
389
+ <path d="M53,90 L54.154,90" id="Fill-74" fill="#A6CFE4"></path>
390
+ <polygon id="Fill-78" fill="#0073AA" points="53 89.5 54.154 89.5 54.154 88 53 88"></polygon>
391
+ <path d="M17,103.21 L50.716,103.21 L50.716,64.224 C50.716,62.995 49.72,62 48.492,62 L19.224,62 C17.995,62 17,62.995 17,64.224 L17,103.21" id="Fill-80" fill="#FFFFFF"></path>
392
+ <path d="M18.5,99.476 L17,99.476 L17,65.686 C17,63.654 18.653,62 20.685,62 L24.907,62 L24.907,63.5 L20.685,63.5 C19.48,63.5 18.5,64.481 18.5,65.686 L18.5,99.476" id="Fill-81" fill="#0073AA"></path>
393
+ <polygon id="Fill-82" fill="#0073AA" points="27 63.5 31.912 63.5 31.912 62 27 62"></polygon>
394
+ <path d="M52.366,99.476 L50.866,99.476 L50.866,65.686 C50.866,64.481 49.886,63.5 48.681,63.5 L34,63.5 L34,62 L48.681,62 C50.712,62 52.366,63.654 52.366,65.686 L52.366,99.476" id="Fill-83" fill="#0073AA"></path>
395
+ <polygon id="Fill-84" fill="#E7F7FF" points="20 103.024 48.147 103.024 48.147 68 20 68"></polygon>
396
+ <polygon id="Fill-85" fill="#0073AA" points="31 66.5 38.152 66.5 38.152 65 31 65"></polygon>
397
+ <path d="M53.497,102.743 L53.497,101.63 C53.497,100.547 52.62,99.666 51.535,99.666 L48.487,99.654 C48.125,98.069 47.502,96.586 46.661,95.257 L48.819,93.097 C49.593,92.329 49.597,91.078 48.826,90.31 L47.211,88.693 C46.442,87.925 45.193,87.919 44.423,88.687 L42.261,90.847 C40.933,90.005 39.451,89.383 37.871,89.023 L37.871,85.973 C37.871,84.884 36.985,84 35.893,84 L33.614,84 C32.522,84 31.639,84.884 31.639,85.973 L31.639,89.023 C30.08,89.374 28.626,89.989 27.31,90.804 L25.164,88.639 C24.396,87.867 23.145,87.852 22.374,88.626 L20.75,90.231 C19.98,90.993 19.971,92.246 20.736,93.018 L22.88,95.189 C22.035,96.513 21.404,97.995 21.04,99.575 L17.991,99.562 C16.899,99.553 16.015,100.438 16.015,101.533 L16,102.743 L28.493,102.743 C28.493,99.288 31.296,96.495 34.756,96.495 C38.211,96.495 41.01,99.288 41.01,102.743 L53.497,102.743" id="Fill-86" fill="#8AC4E6"></path>
398
+ <path d="M41.511,102.493 L40.011,102.493 C40.011,99.462 37.541,96.995 34.506,96.995 C31.466,96.995 28.993,99.462 28.993,102.493 L27.493,102.493 C27.493,98.634 30.639,95.495 34.506,95.495 C38.368,95.495 41.511,98.634 41.511,102.493 Z M16.5,102.503 L15,102.484 L15.015,101.273 C15.015,100.549 15.302,99.862 15.821,99.346 C16.336,98.835 17.001,98.568 17.747,98.563 L20.206,98.573 C20.544,97.337 21.039,96.155 21.684,95.049 L19.952,93.295 C19.443,92.78 19.163,92.095 19.167,91.366 C19.172,90.639 19.457,89.958 19.972,89.448 L21.596,87.842 C22.615,86.82 24.415,86.824 25.446,87.86 L27.18,89.611 C28.283,88.986 29.442,88.508 30.639,88.186 L30.639,85.723 C30.639,84.222 31.861,83 33.364,83 L35.643,83 C37.147,83 38.371,84.222 38.371,85.723 L38.371,88.187 C39.61,88.522 40.792,89.012 41.897,89.651 L43.643,87.906 C44.704,86.849 46.431,86.852 47.491,87.913 L49.106,89.53 C49.619,90.041 49.902,90.724 49.902,91.451 C49.901,92.18 49.615,92.865 49.098,93.38 L47.357,95.121 C47.996,96.231 48.488,97.415 48.824,98.656 L51.288,98.666 C52.781,98.666 53.997,99.883 53.997,101.38 L53.997,102.493 L52.497,102.493 L52.497,101.38 C52.497,100.711 51.954,100.166 51.285,100.166 L47.638,100.151 L47.506,99.571 C47.167,98.087 46.586,96.686 45.778,95.407 L45.457,94.901 L48.039,92.317 C48.273,92.084 48.401,91.776 48.402,91.449 C48.402,91.125 48.276,90.82 48.046,90.591 L46.43,88.974 C45.953,88.495 45.177,88.494 44.703,88.968 L42.116,91.551 L41.61,91.23 C40.336,90.423 38.937,89.842 37.454,89.505 L36.871,89.371 L36.871,85.723 C36.871,85.049 36.32,84.5 35.643,84.5 L33.364,84.5 C32.688,84.5 32.139,85.049 32.139,85.723 L32.139,89.373 L31.553,89.505 C30.122,89.827 28.742,90.394 27.454,91.192 L26.947,91.506 L24.382,88.917 C23.921,88.454 23.115,88.445 22.655,88.905 L21.027,90.514 C20.797,90.742 20.669,91.047 20.667,91.375 C20.666,91.702 20.79,92.01 21.019,92.24 L23.585,94.838 L23.262,95.343 C22.447,96.62 21.861,98.016 21.521,99.494 L21.386,100.078 L17.738,100.063 C17.365,100.068 17.106,100.184 16.877,100.412 C16.644,100.643 16.515,100.952 16.515,101.282 L16.5,102.503 L16.5,102.503 Z" id="Fill-87" fill="#0073AA"></path>
399
+ <path d="M41,34.935 L41,34.935 C41,33.977 41.683,33.179 42.588,33 C41.683,33.179 41,33.978 41,34.936" id="Fill-97" fill="#B4D7E7"></path>
400
+ <path d="M41.75,50.13 L41.75,49.867 L41,49.867 L41,33.973 C41,33.016 41.683,32.217 42.588,32.038 C42.713,32.013 42.842,32 42.974,32 L53.999,32 C54.184,33.539 54.164,35.105 53.921,36.652 L45.341,36.652 L45.341,48.704 C44.19,49.324 42.985,49.799 41.75,50.13" id="Fill-98" fill="#4B97C6"></path>
401
+ <path d="M41,50.593 L41,50 L42.5,50 L42.5,50.263 C42.005,50.396 41.504,50.506 41,50.593" id="Fill-99" fill="#0073AA"></path>
402
+ <path d="M57.996,51.046 C57.881,51.046 57.765,51.027 57.652,50.987 L48.182,47.647 C47.483,48.178 46.753,48.646 46,49.052 L46,37 L54.58,37 C54.365,38.372 53.975,39.729 53.4,41.036 L58.863,49.448 C59.105,49.82 59.083,50.303 58.809,50.652 C58.608,50.906 58.307,51.046 57.996,51.046" id="Fill-100" fill="#E7F7FF"></path>
403
+ <path d="M26.1809572,45.6108842 C19.2089572,40.1958842 17.9459572,30.1538842 23.3599572,23.1818842 C28.7739572,16.2088842 38.8159572,14.9458842 45.7889572,20.3598842 C51.8099572,25.0348842 53.5689572,33.1588842 50.4529572,39.7798842 L56.1399572,48.5368842 C56.2879572,48.7638842 56.0629572,49.0498842 55.8069572,48.9598842 L45.9509572,45.4828842 C40.3129572,49.9858842 32.1309572,50.2308842 26.1809572,45.6108842" id="Fill-101" fill="#FFFFFF"></path>
404
+ <path d="M35.7222614,49.4751973 C32.1062614,49.4751973 28.4892614,48.2991973 25.4552614,45.9421973 C18.5682614,40.5951973 16.9512614,30.7251973 21.7722614,23.4741973 L23.0212614,24.3041973 C18.6322614,30.9051973 20.1062614,39.8901973 26.3752614,44.7581973 C31.9552614,49.0911973 39.7022614,49.0401973 45.2152614,44.6361973 L45.5412614,44.3761973 L54.4992614,47.5361973 L49.3302614,39.5781973 L49.5072614,39.2001973 C52.5142614,32.8101973 50.6452614,25.0271973 45.0622614,20.6911973 C40.8452614,17.4171973 35.1332614,16.5931973 30.1532614,18.5391973 L29.6082614,17.1431973 C35.0762614,15.0031973 41.3502614,15.9101973 45.9822614,19.5071973 C52.0032614,24.1821973 54.0922614,32.5111973 51.0392614,39.4551973 L56.5022614,47.8671973 C56.7442614,48.2381973 56.7222614,48.7221973 56.4482614,49.0701973 C56.1742614,49.4191973 55.7082614,49.5531973 55.2912614,49.4061973 L45.8212614,46.0661973 C42.8302614,48.3371973 39.2762614,49.4751973 35.7222614,49.4751973" id="Fill-102" fill="#0073AA"></path>
405
+ <path d="M25.127,22.451 L24,21.462 C24.279,21.143 24.568,20.838 24.866,20.546 C25.873,19.559 27.002,18.702 28.224,18 L28.971,19.301 C27.86,19.94 26.832,20.719 25.916,21.617 C25.645,21.884 25.382,22.161 25.127,22.451" id="Fill-103" fill="#0073AA"></path>
406
+ <polyline id="Fill-112" fill="#0073AA" points="54.009 33.51 54 32.01 55.647 32 55.656 33.5 54.009 33.51"></polyline>
407
+ <polygon id="Fill-125" fill="#0073AA" points="0 103.5 2.739 103.5 2.739 102 0 102"></polygon>
408
+ <polygon id="Fill-127" fill="#3378B0" points="4 103.5 167.822 103.5 167.822 102 4 102"></polygon>
409
+ <g id="Group" transform="translate(25.000000, 22.000000)">
410
+ <path d="M4.50393103,3.19910345 L2.73937931,4.96289655 L2.16206897,4.38634483 C1.84193103,4.0662069 1.84193103,3.54731034 2.16206897,3.22717241 L2.76744828,2.6217931 C3.08758621,2.30165517 3.60648276,2.30165517 3.92662069,2.6217931 L4.50393103,3.19910345" id="Fill-132" fill="#9CC9E1"></path>
411
+ <path d="M3.34703448,2.95103448 L3.17027586,3.02386207 L2.56489655,3.62924138 L2.49131034,3.80675862 L2.56489655,3.98427586 L2.73937931,4.15875862 L3.69903448,3.19910345 L3.52455172,3.02462069 L3.34703448,2.95103448 Z M2.73937931,5.7677931 L1.76,4.78841379 C1.49751724,4.52668966 1.35337931,4.17772414 1.35337931,3.80675862 C1.35337931,3.4357931 1.49827586,3.08682759 1.76075862,2.82434483 L2.36537931,2.21972414 C2.88882759,1.69551724 3.80448276,1.694 4.32944828,2.21972414 L5.30806897,3.19910345 L2.73937931,5.7677931 L2.73937931,5.7677931 Z" id="Fill-133" fill="#0073AA"></path>
412
+ <path d="M18.8752414,4.77855172 L17.1129655,3.01248276 L17.6902759,2.43593103 C18.0111724,2.11655172 18.530069,2.11731034 18.8502069,2.43744828 L19.454069,3.04282759 C19.7742069,3.36372414 19.7734483,3.88262069 19.4525517,4.20275862 L18.8752414,4.77855172" id="Fill-134" fill="#9CC9E1"></path>
413
+ <path d="M17.9171034,3.01324138 L18.876,3.97441379 L19.0504828,3.79993103 L19.124069,3.62241379 L19.0512414,3.44489655 L18.4473793,2.83951724 C18.3487586,2.74089655 18.1902069,2.74165517 18.0923448,2.83875862 L17.9171034,3.01324138 Z M18.8744828,5.58344828 L16.308069,3.01172414 L17.2889655,2.03310345 C17.8306207,1.49296552 18.7113793,1.49372414 19.2530345,2.03537931 L19.8568966,2.64151724 C20.1193793,2.904 20.2627586,3.25296552 20.262,3.62393103 C20.262,3.99489655 20.1171034,4.34386207 19.8538621,4.60558621 L18.8744828,5.58344828 L18.8744828,5.58344828 Z" id="Fill-135" fill="#0073AA"></path>
414
+ <path d="M17.2783448,19.1483448 L19.0466897,17.3883448 L19.6224828,17.9664138 C19.9418621,18.2873103 19.9403448,18.8062069 19.6194483,19.1255862 L19.0133103,19.7294483 C18.6924138,20.0488276 18.1727586,20.0473103 17.8533793,19.7264138 L17.2783448,19.1483448" id="Fill-136" fill="#9CC9E1"></path>
415
+ <path d="M18.0824828,19.1506207 L18.2569655,19.3251034 L18.4344828,19.3994483 L18.6112414,19.3258621 L19.2181379,18.7227586 L19.2917241,18.5452414 L19.2188966,18.3677241 L19.0451724,18.1924828 L18.0824828,19.1506207 Z M18.4344828,20.5373793 L18.4314483,20.5373793 C18.0604828,20.5366207 17.7115172,20.3909655 17.4505517,20.1277241 L16.4734483,19.1468276 L19.0482069,16.5834483 L20.0253103,17.5651034 C20.2870345,17.8283448 20.4311724,18.1773103 20.4296552,18.5482759 C20.4288966,18.9192414 20.2832414,19.2674483 20.0207586,19.5291724 L19.4146207,20.1322759 C19.1521379,20.394 18.8046897,20.5373793 18.4344828,20.5373793 L18.4344828,20.5373793 Z" id="Fill-137" fill="#0073AA"></path>
416
+ <path d="M2.91006897,17.534 L4.66855172,19.3046207 L4.08972414,19.8796552 C3.76806897,20.1990345 3.24917241,20.1967586 2.93055172,19.8758621 L2.32744828,19.2682069 C2.00806897,18.9473103 2.01034483,18.4284138 2.33124138,18.1090345 L2.91006897,17.534" id="Fill-138" fill="#9CC9E1"></path>
417
+ <path d="M2.9077931,18.3388966 L2.73255172,18.5126207 L2.6582069,18.6901379 L2.73103448,18.8668966 L3.33413793,19.4745517 L3.51089655,19.5488966 L3.51165517,19.5488966 L3.68841379,19.476069 L3.86365517,19.3023448 L2.9077931,18.3388966 Z M3.51165517,20.6868276 L3.50710345,20.6868276 C3.13613793,20.6853103 2.78717241,20.5396552 2.5262069,20.2764138 L1.92386207,19.6695172 C1.66213793,19.4062759 1.51875862,19.0565517 1.52027586,18.6863448 C1.52103448,18.3146207 1.66744828,17.9664138 1.93068966,17.7046897 L2.91310345,16.7298621 L5.47268966,19.3084138 L4.49027586,20.2832414 C4.22855172,20.5434483 3.88110345,20.6868276 3.51165517,20.6868276 L3.51165517,20.6868276 Z" id="Fill-139" fill="#0073AA"></path>
418
+ <path d="M10.9006207,16.8975172 C7.58772414,16.8975172 4.90296552,14.2127586 4.90296552,10.9006207 C4.90296552,7.58848276 7.58772414,4.90296552 10.9006207,4.90296552 C14.2127586,4.90296552 16.8975172,7.58848276 16.8975172,10.9006207 C16.8975172,14.2127586 14.2127586,16.8975172 10.9006207,16.8975172 Z M10.9006207,0.568965517 C5.19427586,0.568965517 0.568965517,5.19427586 0.568965517,10.9006207 C0.568965517,16.6062069 5.19427586,21.2322759 10.9006207,21.2322759 C16.6062069,21.2322759 21.2315172,16.6062069 21.2315172,10.9006207 C21.2315172,5.19427586 16.6062069,0.568965517 10.9006207,0.568965517 L10.9006207,0.568965517 Z" id="Fill-140" fill="#FFFFFF"></path>
419
+ <path d="M10.9006207,5.47193103 C7.90710345,5.47193103 5.47193103,7.90710345 5.47193103,10.9006207 C5.47193103,13.8933793 7.90710345,16.3285517 10.9006207,16.3285517 C13.8933793,16.3285517 16.3285517,13.8933793 16.3285517,10.9006207 C16.3285517,7.90710345 13.8933793,5.47193103 10.9006207,5.47193103 Z M10.9006207,17.4664828 C7.27972414,17.4664828 4.334,14.5207586 4.334,10.9006207 C4.334,7.27972414 7.27972414,4.334 10.9006207,4.334 C14.5207586,4.334 17.4664828,7.27972414 17.4664828,10.9006207 C17.4664828,14.5207586 14.5207586,17.4664828 10.9006207,17.4664828 Z M10.9006207,1.13793103 C5.51744828,1.13793103 1.13793103,5.51744828 1.13793103,10.9006207 C1.13793103,16.2837931 5.51744828,20.6633103 10.9006207,20.6633103 C16.2830345,20.6633103 20.6625517,16.2837931 20.6625517,10.9006207 C20.6625517,5.51744828 16.2830345,1.13793103 10.9006207,1.13793103 Z M10.9006207,21.8012414 C4.89006897,21.8012414 0,16.9111724 0,10.9006207 C0,4.89006897 4.89006897,0 10.9006207,0 C16.9111724,0 21.8004828,4.89006897 21.8004828,10.9006207 C21.8004828,16.9111724 16.9111724,21.8012414 10.9006207,21.8012414 L10.9006207,21.8012414 Z" id="Fill-141" fill="#0073AA"></path>
420
+ <path d="M1.59386207,6.43613793 L5.45524138,8.39868966 C6.00144828,7.2122069 6.91862069,6.23206897 8.06413793,5.61606897 L6.09324138,1.75924138 C4.13448276,2.79172414 2.5557931,4.43565517 1.59386207,6.43613793" id="Fill-142" fill="#D1E8F0"></path>
421
+ <path d="M15.6867586,1.74937931 L13.7242069,5.61 C14.8712414,6.22372414 15.7906897,7.20158621 16.339931,8.38655172 L20.1967586,6.41565517 C19.2310345,4.41744828 17.6477931,2.77731034 15.6867586,1.74937931" id="Fill-143" fill="#D1E8F0"></path>
422
+ <path d="M20.0010345,15.7914483 L16.1335862,13.8258621 C15.5357931,14.8917241 14.6216552,15.7527586 13.5186207,16.2898621 L15.4895172,20.1466897 C17.4057931,19.1931034 18.9882759,17.672069 20.0010345,15.7914483" id="Fill-144" fill="#D1E8F0"></path>
423
+ <path d="M6.33068966,20.1557931 L8.29324138,16.2951724 C7.18944828,15.7603448 6.2737931,14.9015862 5.67372414,13.8364828 L1.80931034,15.811931 C2.82586207,17.6895172 4.41289655,19.2067586 6.33068966,20.1557931" id="Fill-145" fill="#D1E8F0"></path>
424
+ <path d="M2.36234483,6.18806897 L5.20944828,7.63551724 C5.72606897,6.73731034 6.45206897,5.96655172 7.31537931,5.40062069 L5.85731034,2.54668966 C4.40455172,3.4282069 3.18924138,4.69510345 2.36234483,6.18806897 Z M5.72455172,9.174 L0.840551724,6.69103448 L1.0817931,6.18882759 C2.09075862,4.08896552 3.77717241,2.33731034 5.82848276,1.25627586 L6.33827586,0.987724138 L8.82427586,5.85351724 L8.33344828,6.11751724 C7.30324138,6.67131034 6.4642069,7.56572414 5.97186207,8.63613793 L5.72455172,9.174 L5.72455172,9.174 Z" id="Fill-146" fill="#0073AA"></path>
425
+ <path d="M14.4729655,5.39303448 C15.3370345,5.95668966 16.0653103,6.72593103 16.5834483,7.62337931 L19.4275172,6.16986207 C18.5983448,4.67841379 17.3792414,3.41455172 15.9249655,2.53682759 L14.4729655,5.39303448 Z M16.0721379,9.16262069 L15.8233103,8.62551724 C15.3286897,7.55662069 14.4881379,6.66372414 13.4556552,6.11144828 L12.964069,5.84896552 L15.4402069,0.977862069 L15.9507586,1.24565517 C18.0051034,2.32213793 19.6945517,4.07075862 20.7088276,6.16834483 L20.9508276,6.66903448 L16.0721379,9.16262069 L16.0721379,9.16262069 Z" id="Fill-147" fill="#0073AA"></path>
426
+ <path d="M14.278,16.5250345 L15.7330345,19.3728966 C17.1630345,18.5588966 18.3495172,17.4156552 19.2082759,16.0266207 L16.3422069,14.570069 C15.8058621,15.3658621 15.1041379,16.0304138 14.278,16.5250345 Z M15.2391724,20.9068276 L12.7478621,16.0326897 L13.2697931,15.7785517 C14.2749655,15.2892414 15.0935172,14.5177241 15.6366897,13.5474483 L15.9037241,13.0710345 L20.7778621,15.547931 L20.5017241,16.0615172 C19.4297931,18.0521379 17.7835862,19.6406897 15.7428966,20.6557241 L15.2391724,20.9068276 L15.2391724,20.9068276 Z" id="Fill-148" fill="#0073AA"></path>
427
+ <path d="M2.60282759,16.0455862 C3.46310345,17.4315862 4.65337931,18.5710345 6.08565517,19.3827586 L7.53462069,16.5318621 C6.70772414,16.0387586 6.00524138,15.3764828 5.46662069,14.5822069 L2.60282759,16.0455862 Z M6.58331034,20.9151724 L6.07882759,20.6663448 C4.03358621,19.6543448 2.38510345,18.0695862 1.30937931,16.0827586 L1.03172414,15.569931 L5.90206897,13.0816552 L6.16986207,13.5573103 C6.71531034,14.5268276 7.53537931,15.296069 8.54131034,15.7823448 L9.06324138,16.0357241 L6.58331034,20.9151724 L6.58331034,20.9151724 Z" id="Fill-149" fill="#0073AA"></path>
428
+ </g>
429
+ </g>
430
+ </symbol>
431
+
432
+
433
+ <symbol id="icon-content" width="175px" height="110px" viewBox="0 0 175 110" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
434
+ <defs></defs>
435
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
436
+ <g id="Group-2" transform="translate(55.000000, 43.000000)">
437
+ <polygon id="Fill-148" fill="#0073AA" points="0.559516803 47.1133533 7.64844949 47.1133533 7.64844949 45.8015 0.559516803 45.8015"></polygon>
438
+ <path d="M53.8269776,52.3664477 L53.8269776,4.41821002 C53.8269776,2.9725477 52.6187393,1.8015 51.1291798,1.8015 L11.2573146,1.8015 C9.76775514,1.8015 8.5595168,2.9725477 8.5595168,4.41821002 L8.5595168,52.3664477 L53.8269776,52.3664477" id="Fill-159" fill="#FFFFFF"></path>
439
+ <path d="M8,10.1133533 L8,4.49781548 C8,3.00819763 9.01618254,1.8015 10.2686027,1.8015 L50.9988581,1.8015 C52.2512783,1.8015 53.2674608,3.00819763 53.2674608,4.49781548 L53.2674608,10.1133533 L8,10.1133533" id="Fill-160" fill="#E7F7FF"></path>
440
+ <polygon id="Fill-161" fill="#0073AA" points="8.5595168 10.1133533 53.8269776 10.1133533 53.8269776 8.8015 8.5595168 8.8015"></polygon>
441
+ <polygon id="Fill-165" fill="#E7F7FF" points="12 32.1133533 39.6977074 32.1133533 39.6977074 12.8015 12 12.8015"></polygon>
442
+ <polygon id="Fill-165" fill="#E7F7FF" points="41 25.4801247 49.6977074 25.4801247 49.6977074 12.8015 41 12.8015"></polygon>
443
+ <polygon id="Fill-166" fill="#D1E8F0" points="15.5595168 46.4801247 25.6356835 46.4801247 25.6356835 33.8015 15.5595168 33.8015"></polygon>
444
+ <path d="M7.5595168,52.0232489 L7.5595168,4.07501124 C7.5595168,2.26990111 9.07342141,0.8015 10.9335674,0.8015 L50.8054326,0.8015 C52.6655786,0.8015 54.1794832,2.26990111 54.1794832,4.07501124 L54.1794832,31.0259048 L52.8269776,31.0259048 L52.8269776,4.07501124 C52.8269776,2.99316956 51.9198972,2.11335329 50.8054326,2.11335329 L10.9335674,2.11335329 C9.81910283,2.11335329 8.9120224,2.99316956 8.9120224,4.07501124 L8.9120224,52.0232489 L7.5595168,52.0232489 Z" id="Fill-168" fill="#0073AA"></path>
445
+ </g>
446
+ <path d="M61.309,91.654 C61.309,95.881 57.882,99.308 53.655,99.308 C49.427,99.308 46,95.881 46,91.654 C46,87.427 49.427,84 53.655,84 C57.882,84 61.309,87.427 61.309,91.654" id="Fill-406" fill="#FFFFFF"></path>
447
+ <path d="M118.91,95.455 C118.91,100.125 115.124,103.91 110.455,103.91 C105.785,103.91 102,100.125 102,95.455 C102,90.786 105.785,87 110.455,87 C115.124,87 118.91,90.786 118.91,95.455" id="Fill-407" fill="#FFFFFF"></path>
448
+ <path d="M139.063,95.531 C139.063,102.452 133.452,108.062 126.532,108.062 C119.611,108.062 114,102.452 114,95.531 C114,88.61 119.611,83 126.532,83 C133.452,83 139.063,88.61 139.063,95.531" id="Fill-408" fill="#FFFFFF"></path>
449
+ <path d="M150.687,100.844 C150.687,105.175 147.175,108.687 142.843,108.687 C138.512,108.687 135,105.175 135,100.844 C135,96.512 138.512,93 142.843,93 C147.175,93 150.687,96.512 150.687,100.844" id="Fill-409" fill="#FFFFFF"></path>
450
+ <path d="M168.34,95.67 C168.34,102.667 162.668,108.34 155.67,108.34 C148.673,108.34 143,102.667 143,95.67 C143,88.672 148.673,83 155.67,83 C162.668,83 168.34,88.672 168.34,95.67" id="Fill-410" fill="#FFFFFF"></path>
451
+ <path d="M48.999,92 C48.999,97.522 44.522,101.999 39,101.999 C33.477,101.999 29,97.522 29,92 C29,86.477 33.477,82 39,82 C44.522,82 48.999,86.477 48.999,92" id="Fill-411" fill="#FFFFFF"></path>
452
+ <path d="M63.999,99 C63.999,104.522 59.522,108.999 54,108.999 C48.477,108.999 44,104.522 44,99 C44,93.477 48.477,89 54,89 C59.522,89 63.999,93.477 63.999,99" id="Fill-412" fill="#FFFFFF"></path>
453
+ <path d="M26.76,100.88 C26.76,105.785 22.784,109.76 17.88,109.76 C12.976,109.76 9,105.785 9,100.88 C9,95.976 12.976,92 17.88,92 C22.784,92 26.76,95.976 26.76,100.88" id="Fill-413" fill="#FFFFFF"></path>
454
+ <path d="M38.02,100.01 C38.02,103.882 34.881,107.02 31.01,107.02 C27.138,107.02 24,103.882 24,100.01 C24,96.139 27.138,93 31.01,93 C34.881,93 38.02,96.139 38.02,100.01" id="Fill-415" fill="#FFFFFF"></path>
455
+ <path d="M61.807,91.405 L60.307,91.405 C60.307,87.598 57.211,84.5 53.404,84.5 C49.597,84.5 46.5,87.598 46.5,91.405 L45,91.405 C45,86.77 48.77,83 53.404,83 C58.038,83 61.807,86.77 61.807,91.405" id="Fill-416" fill="#0073AA"></path>
456
+ <path d="M120.41,96.205 L118.91,96.205 C118.91,91.957 115.454,88.5 111.205,88.5 C106.956,88.5 103.5,91.957 103.5,96.205 L102,96.205 C102,91.13 106.129,87 111.205,87 C116.281,87 120.41,91.13 120.41,96.205 L131.820312,88.6435547" id="Fill-417" fill="#0073AA"></path>
457
+ <path d="M158.3625,49.5 L154.0885,49.5 C153.6735,49.5 153.3385,49.164 153.3385,48.75 C153.3385,48.336 153.6735,48 154.0885,48 L158.3625,48 C158.7765,48 159.1125,48.336 159.1125,48.75 C159.1125,49.164 158.7765,49.5 158.3625,49.5" id="Fill-418" fill="#0073AA"></path>
458
+ <path d="M156.0885,51.774 C155.6745,51.774 155.3385,51.438 155.3385,51.024 L155.3385,46.75 C155.3385,46.336 155.6745,46 156.0885,46 C156.5025,46 156.8385,46.336 156.8385,46.75 L156.8385,51.024 C156.8385,51.438 156.5025,51.774 156.0885,51.774" id="Fill-419" fill="#0073AA"></path>
459
+ <path d="M25.025,7.5 L20.75,7.5 C20.336,7.5 20,7.164 20,6.75 C20,6.336 20.336,6 20.75,6 L25.025,6 C25.439,6 25.775,6.336 25.775,6.75 C25.775,7.164 25.439,7.5 25.025,7.5" id="Fill-420" fill="#0073AA"></path>
460
+ <path d="M22.75,9.774 C22.336,9.774 22,9.438 22,9.024 L22,4.75 C22,4.336 22.336,4 22.75,4 C23.164,4 23.5,4.336 23.5,4.75 L23.5,9.024 C23.5,9.438 23.164,9.774 22.75,9.774" id="Fill-421" fill="#0073AA"></path>
461
+ <path d="M100.74975,4.52275 C100.55775,4.52275 100.36575,4.44875 100.21975,4.30275 C99.92675,4.00975 99.92675,3.53475 100.21975,3.24175 L103.24175,0.21975 C103.53475,-0.07325 104.00975,-0.07325 104.30275,0.21975 C104.59575,0.51275 104.59575,0.98675 104.30275,1.27975 L101.27975,4.30275 C101.13375,4.44875 100.94175,4.52275 100.74975,4.52275" id="Fill-424" fill="#0073AA"></path>
462
+ <path d="M103.77175,4.52275 C103.57975,4.52275 103.38875,4.44875 103.24175,4.30275 L100.21975,1.27975 C99.92675,0.98675 99.92675,0.51275 100.21975,0.21975 C100.51275,-0.07325 100.98675,-0.07325 101.27975,0.21975 L104.30275,3.24175 C104.59575,3.53475 104.59575,4.00975 104.30275,4.30275 C104.15575,4.44875 103.96475,4.52275 103.77175,4.52275" id="Fill-425" fill="#0073AA"></path>
463
+ <path d="M10.74975,61.52275 C10.55775,61.52275 10.36575,61.44875 10.21975,61.30275 C9.92675,61.00975 9.92675,60.53475 10.21975,60.24175 L13.24175,57.21975 C13.53475,56.92675 14.00975,56.92675 14.30275,57.21975 C14.59575,57.51275 14.59575,57.98675 14.30275,58.27975 L11.27975,61.30275 C11.13375,61.44875 10.94175,61.52275 10.74975,61.52275" id="Fill-424" fill="#0073AA"></path>
464
+ <path d="M13.77175,61.52275 C13.57975,61.52275 13.38875,61.44875 13.24175,61.30275 L10.21975,58.27975 C9.92675,57.98675 9.92675,57.51275 10.21975,57.21975 C10.51275,56.92675 10.98675,56.92675 11.27975,57.21975 L14.30275,60.24175 C14.59575,60.53475 14.59575,61.00975 14.30275,61.30275 C14.15575,61.44875 13.96475,61.52275 13.77175,61.52275" id="Fill-425" fill="#0073AA"></path>
465
+ <polygon id="Fill-426" fill="#0073AA" points="142 69.5 163.817 69.5 163.817 68 142 68"></polygon>
466
+ <polygon id="Fill-428" fill="#0073AA" points="32 7.5 72.796 7.5 72.796 6 32 6"></polygon>
467
+ <polygon id="Fill-429" fill="#0073AA" points="125 8.5 146.817 8.5 146.817 7 125 7"></polygon>
468
+ <polygon id="Fill-430" fill="#0073AA" points="119 8.5 122.912 8.5 122.912 7 119 7"></polygon>
469
+ <path d="M139.677,74.338 C139.677,75.077 139.078,75.676 138.339,75.676 C137.599,75.676 137,75.077 137,74.338 C137,73.599 137.599,73 138.339,73 C139.078,73 139.677,73.599 139.677,74.338" id="Fill-433" fill="#0073AA"></path>
470
+ <path d="M14.677,18.338 C14.677,19.077 14.078,19.676 13.339,19.676 C12.6,19.676 12,19.077 12,18.338 C12,17.599 12.6,17 13.339,17 C14.078,17 14.677,17.599 14.677,18.338" id="Fill-434" fill="#0073AA"></path>
471
+ <path d="M164.677,26.339 C164.677,27.078 164.078,27.677 163.339,27.677 C162.6,27.677 162,27.078 162,26.339 C162,25.599 162.6,25 163.339,25 C164.078,25 164.677,25.599 164.677,26.339" id="Fill-435" fill="#0073AA"></path>
472
+ <path d="M64.696,28.848 C64.696,29.317 64.317,29.696 63.848,29.696 C63.38,29.696 63,29.317 63,28.848 C63,28.38 63.38,28 63.848,28 C64.317,28 64.696,28.38 64.696,28.848" id="Fill-438" fill="#0073AA"></path>
473
+ <polygon id="Fill-478" fill="#8AC4E6" points="60 109.352 67.384 109.352 67.384 75 60 75"></polygon>
474
+ <path d="M60.5,75.5 L60.5,107.257 C60.5,107.861 60.991,108.352 61.595,108.352 L66.384,108.352 L66.384,75.5 L60.5,75.5 Z M67.884,109.467529 L61.5241699,109.467529 C60.0931699,109.467529 59,108.688 59,107.257 L59,74 L67.884,74 L67.884,109.852 L67.884,109.467529 Z" id="Fill-479" fill="#0073AA"></path>
475
+ <polyline id="Fill-480" fill="#E7F7FF" points="56 79.993 63.384 79.993 65.78 75 58.396 75 56 79.993"></polyline>
476
+ <path d="M57.383,78.992 L63.104,78.992 L64.78,75.5 L59.059,75.5 L57.383,78.992 Z M64.047,80.492 L55,80.492 L58.115,74 L67.164,74 L64.047,80.492 L64.047,80.492 Z" id="Fill-481" fill="#0073AA"></path>
477
+ <path d="M109.971,109.352 L68,109.352 L68,75 L112.58,75 L112.58,106.742 C112.58,108.183 111.412,109.352 109.971,109.352" id="Fill-482" fill="#0073AA"></path>
478
+ <polygon id="Fill-483" fill="#0073AA" points="111 99.565 112.5 99.565 112.5 94 111 94"></polygon>
479
+ <path d="M67,109.434082 L67,74 L113.081,74 L113.081,90.582 L111.581,90.582 L111.581,75.5 L68.5,75.5 L68.5,108.352 L109.398,108.352 C110.601,108.352 111.581,107.373 111.581,106.17 L111.581,103.128 L113.081,103.128 L113.081,106.17 C113.081,108.2 111.732416,109.434082 109.701416,109.434082 L67,109.434082 Z" id="Fill-484" fill="#0073AA"></path>
480
+ <polyline id="Fill-485" fill="#E7F7FF" points="115.976 79.993 71.396 79.993 69 75 113.58 75 115.976 79.993"></polyline>
481
+ <path d="M72.059,78.992 L114.976,78.992 L113.3,75.5 L70.383,75.5 L72.059,78.992 Z M117.361,80.492 L71.116,80.492 L68,74 L114.244,74 L117.361,80.492 L117.361,80.492 Z" id="Fill-486" fill="#0073AA"></path>
482
+ <polyline id="Fill-513" fill="#D1E8F0" points="132.7655 45.13 119.8855 45.13 117.7395 48.848 126.3255 58.614 134.9125 48.848 132.7655 45.13"></polyline>
483
+ <polyline id="Fill-514" fill="#9CC9E1" points="122.8485 48.848 120.7015 45.13 118.7395 48.875 122.8485 48.848"></polyline>
484
+ <polyline id="Fill-515" fill="#9CC9E1" points="126.8965 48.733 124.8155 45.13 122.7395 48.727 126.8965 48.733"></polyline>
485
+ <polyline id="Fill-516" fill="#9CC9E1" points="130.8655 48.721 128.6355 45.13 126.7395 48.748 130.8655 48.721"></polyline>
486
+ <polyline id="Fill-517" fill="#9CC9E1" points="126.7395 49.13 126.7395 58.246 130.9345 49.13 126.7395 49.13"></polyline>
487
+ <polyline id="Fill-518" fill="#62A6CF" points="118.7395 49.13 118.7395 49.221 126.9455 58.451 122.6585 49.13 118.7395 49.13"></polyline>
488
+ <polyline id="Fill-519" fill="#62A6CF" points="134.9465 49.13 134.9465 49.221 126.7395 58.451 131.0275 49.13 134.9465 49.13"></polyline>
489
+ <polyline id="Fill-520" fill="#0073AA" points="126.2435 59.5 116.7395 48.69 119.3715 44.13 129.2805 44.13 129.2805 45.63 120.2375 45.63 118.5765 48.507 126.2435 57.229 133.9115 48.507 132.2515 45.63 131.4435 45.63 131.4435 44.13 133.1165 44.13 135.7495 48.69 126.2435 59.5"></polyline>
490
+ <polygon id="Fill-521" fill="#0073AA" points="129.7395 45.63 131.1395 45.63 131.1395 44.13 129.7395 44.13"></polygon>
491
+ <polygon id="Fill-522" fill="#0073AA" points="117.7395 49.63 134.9125 49.63 134.9125 48.13 117.7395 48.13"></polygon>
492
+ <polyline id="Fill-523" fill="#0073AA" points="126.0315 58.5 121.7395 48.734 123.1125 48.13 127.4045 57.897 126.0315 58.5"></polyline>
493
+ <polyline id="Fill-524" fill="#0073AA" points="127.1125 58.5 125.7395 57.896 130.0315 48.13 131.4045 48.734 127.1125 58.5"></polyline>
494
+ <polygon id="Fill-525" fill="#0073AA" points="125.7395 58.673 127.2395 58.673 127.2395 49.13 125.7395 49.13"></polygon>
495
+ <polyline id="Fill-526" fill="#0073AA" points="121.8865 49.598 119.7395 45.88 121.0375 45.13 123.1855 48.848 121.8865 49.598"></polyline>
496
+ <polyline id="Fill-527" fill="#0073AA" points="125.8865 49.598 123.7395 45.88 125.0375 45.13 127.1855 48.848 125.8865 49.598"></polyline>
497
+ <polyline id="Fill-528" fill="#0073AA" points="129.8875 49.598 127.7395 45.88 129.0385 45.13 131.1865 48.848 129.8875 49.598"></polyline>
498
+ <polyline id="Fill-529" fill="#0073AA" points="132.0385 49.598 130.7395 48.848 132.8865 45.13 134.1855 45.88 132.0385 49.598"></polyline>
499
+ <polyline id="Fill-530" fill="#0073AA" points="127.0385 49.598 125.7395 48.848 127.8865 45.13 129.1855 45.88 127.0385 49.598"></polyline>
500
+ <polyline id="Fill-531" fill="#0073AA" points="123.0385 49.598 121.7395 48.848 123.8875 45.13 125.1855 45.88 123.0385 49.598"></polyline>
501
+ <path d="M88.6770083,14.236 C87.2610083,14.236 86.1140083,13.09 86.1140083,11.672 C86.1140083,10.257 87.2610083,9.114 88.6770083,9.114 C90.0920083,9.114 91.2380083,10.257 91.2380083,11.672 C91.2380083,13.09 90.0920083,14.236 88.6770083,14.236 Z M95.5460083,10.412 L94.2980083,10.407 C94.1500083,9.758 93.8950083,9.151 93.5510083,8.607 L94.4340083,7.723 C94.7510083,7.409 94.7530083,6.897 94.4370083,6.582 L93.7760083,5.921 C93.4610083,5.606 92.9500083,5.604 92.6350083,5.918 L91.7500083,6.802 C91.2060083,6.458 90.5990083,6.203 89.9530083,6.056 L89.9530083,4.807 C89.9530083,4.362 89.5900083,4 89.1430083,4 L88.2100083,4 C87.7630083,4 87.4020083,4.362 87.4020083,4.807 L87.4020083,6.056 C86.7640083,6.199 86.1680083,6.451 85.6300083,6.785 L84.7510083,5.898 C84.4370083,5.583 83.9250083,5.576 83.6090083,5.893 L82.9440083,6.55 C82.6290083,6.862 82.6260083,7.375 82.9390083,7.691 L83.8160083,8.58 C83.4700083,9.121 83.2120083,9.728 83.0630083,10.375 L81.8150083,10.37 C81.3680083,10.366 81.0060083,10.728 81.0060083,11.176 L81.0000083,12.11 C80.9980083,12.554 81.3580083,12.918 81.8040083,12.921 L83.0500083,12.925 C83.1970083,13.576 83.4490083,14.181 83.7930083,14.724 L82.9040083,15.609 C82.5900083,15.921 82.5880083,16.432 82.9030083,16.749 L83.5590083,17.411 C83.8750083,17.73 84.3870083,17.73 84.7010083,17.416 L85.5880083,16.537 C86.1320083,16.881 86.7360083,17.141 87.3870083,17.29 L87.3820083,18.535 C87.3800083,18.981 87.7410083,19.346 88.1870083,19.346 L89.1210083,19.349 C89.5670083,19.349 89.9300083,18.991 89.9300083,18.546 L89.9340083,17.297 C90.5830083,17.151 91.1900083,16.895 91.7360083,16.553 L92.6170083,17.44 C92.9310083,17.755 93.4440083,17.755 93.7620083,17.44 L94.4210083,16.781 C94.7370083,16.468 94.7390083,15.955 94.4210083,15.642 L93.5450083,14.755 C93.8890083,14.21 94.1460083,13.606 94.2930083,12.955 L95.5400083,12.959 C95.9870083,12.959 96.3470083,12.598 96.3490083,12.153 L96.3490083,11.216 C96.3490083,10.773 95.9900083,10.412 95.5460083,10.412 L95.5460083,10.412 Z" id="Fill-532" fill="#FFFFFF"></path>
502
+ <path d="M88.4280386,9.614 C87.4270386,9.614 86.6130386,10.425 86.6130386,11.422 C86.6130386,12.422 87.4270386,13.236 88.4280386,13.236 C89.4260386,13.236 90.2370386,12.422 90.2370386,11.422 C90.2370386,10.425 89.4260386,9.614 88.4280386,9.614 Z M88.4280386,14.736 C86.6010386,14.736 85.1130386,13.249 85.1130386,11.422 C85.1130386,9.598 86.6010386,8.114 88.4280386,8.114 C90.2530386,8.114 91.7370386,9.598 91.7370386,11.422 C91.7370386,13.249 90.2530386,14.736 88.4280386,14.736 Z M85.2350386,15.333 L85.7390386,15.653 C86.2280386,15.962 86.7540386,16.183 87.3040386,16.309 L87.8890386,16.442 L87.8820386,18.288 L88.8740386,18.349 L88.9360386,16.446 L89.5200386,16.315 C90.0730386,16.191 90.6010386,15.973 91.0870386,15.668 L91.5950386,15.349 L92.8990386,16.662 L93.6400386,16.002 L92.3430386,14.609 L92.6600386,14.106 C92.9680386,13.618 93.1870386,13.091 93.3120386,12.539 L93.4440386,11.953 L95.2920386,11.959 L95.3490386,10.966 L95.3470386,10.966 L95.2930386,10.912 L93.4480386,10.905 L93.3170386,10.323 C93.1910386,9.767 92.9720386,9.24 92.6670386,8.758 L92.3470386,8.251 L93.6520386,6.943 L92.9950386,6.201 L91.6040386,7.507 L91.0970386,7.186 C90.6190386,6.882 90.0940386,6.664 89.5360386,6.537 L88.9520386,6.404 L88.9520386,4.557 L87.9600386,4.5 L87.9020386,6.406 L87.3170386,6.538 C86.7780386,6.659 86.2590386,6.872 85.7740386,7.172 L85.2660386,7.486 L83.9680386,6.176 L83.2220386,6.833 L84.5210386,8.229 L84.1980386,8.733 C83.8920386,9.213 83.6720386,9.738 83.5430386,10.294 L83.4080386,10.877 L81.5630386,10.87 L81.5000386,11.865 L83.4010386,11.927 L83.5310386,12.511 C83.6580386,13.072 83.8750386,13.597 84.1760386,14.073 L84.4970386,14.581 L83.1840386,15.89 L83.8410386,16.633 L85.2350386,15.333 Z M88.8710386,19.849 L87.9340386,19.845 C87.5210386,19.845 87.1300386,19.683 86.8360386,19.387 C86.5410386,19.091 86.3800386,18.699 86.3820386,18.282 L86.3850386,17.607 C86.0690386,17.502 85.7600386,17.374 85.4610386,17.22 L84.9800386,17.698 C84.6890386,17.989 84.2980386,18.151 83.8830386,18.151 L83.8800386,18.151 C83.4620386,18.15 83.0700386,17.986 82.7760386,17.688 L82.1200386,17.026 C81.5160386,16.417 81.5180386,15.431 82.1260386,14.827 L82.6080386,14.346 C82.4580386,14.05 82.3310386,13.742 82.2280386,13.423 L81.5520386,13.421 C80.6890386,13.414 79.9940386,12.711 80.0000386,11.856 L80.0060386,10.921 C80.0060386,10.507 80.1700386,10.114 80.4670386,9.819 C80.7640386,9.525 81.1360386,9.404 81.5720386,9.37 L82.2450386,9.373 C82.3510386,9.056 82.4800386,8.748 82.6330386,8.451 L82.1550386,7.968 C81.8630386,7.674 81.7040386,7.283 81.7060386,6.866 C81.7070386,6.45 81.8710386,6.059 82.1670386,5.767 L82.8320386,5.109 C83.4110386,4.528 84.4430386,4.528 85.0320386,5.119 L85.5110386,5.603 C85.8010386,5.458 86.0980386,5.335 86.4020386,5.235 L86.4020386,4.557 C86.4020386,3.698 87.1010386,3 87.9600386,3 L88.8930386,3 C89.7530386,3 90.4520386,3.698 90.4520386,4.557 L90.4520386,5.236 C90.7700386,5.339 91.0770386,5.467 91.3730386,5.618 L91.8560386,5.138 C92.1480386,4.845 92.5370386,4.684 92.9530386,4.684 C93.3870386,4.639 93.7620386,4.846 94.0560386,5.14 L94.7170386,5.802 C95.0120386,6.096 95.1730386,6.487 95.1720386,6.904 C95.1720386,7.321 95.0080386,7.712 94.7120386,8.006 L94.2350386,8.484 C94.3870386,8.782 94.5150386,9.091 94.6180386,9.41 L95.2960386,9.412 C96.1520386,9.413 96.8490386,10.109 96.8490386,10.966 L96.8490386,11.902 C96.8450386,12.762 96.1460386,13.459 95.2900386,13.459 L94.6120386,13.457 C94.5080386,13.774 94.3800386,14.083 94.2280386,14.381 L94.7040386,14.864 C94.9940386,15.149 95.1580386,15.541 95.1590386,15.958 C95.1590386,16.377 94.9950386,16.77 94.6970386,17.065 L94.0420386,17.72 C93.4280386,18.327 92.4410386,18.324 91.8360386,17.721 L91.3560386,17.237 C91.0580386,17.388 90.7490386,17.516 90.4320386,17.619 L90.4300386,18.299 C90.4300386,19.152 89.7310386,19.849 88.8710386,19.849 L88.8710386,19.849 Z" id="Fill-533" fill="#0073AA"></path>
503
+ <path d="M38.749,102.498 C32.822,102.498 28,97.676 28,91.749 C28,85.821 32.822,81 38.749,81 C42.75,81 46.394,83.199 48.261,86.739 L46.935,87.439 C45.328,84.392 42.191,82.5 38.749,82.5 C33.649,82.5 29.5,86.649 29.5,91.749 C29.5,96.849 33.649,100.998 38.749,100.998 L38.749,102.498" id="Fill-534" fill="#0073AA"></path>
504
+ <path d="M24.5,99.523 L23,99.523 C23,97.89 23.501,96.328 24.45,95.005 C25.519,93.516 27.08,92.448 28.848,92 L29.217,93.454 C27.791,93.815 26.531,94.677 25.669,95.879 C24.905,96.946 24.5,98.205 24.5,99.523" id="Fill-535" fill="#0073AA"></path>
505
+ <path d="M48.5,102.521 L47,102.521 C47,100.889 47.501,99.327 48.45,98.005 C49.518,96.515 51.079,95.448 52.848,95 L53.217,96.454 C51.79,96.815 50.531,97.676 49.669,98.879 C48.904,99.945 48.5,101.205 48.5,102.521" id="Fill-536" fill="#0073AA"></path>
506
+ <path d="M17.63,109.76 C12.14,109.76 8,105.835 8,100.63 C8,95.32 12.32,91 17.63,91 C20.88,91 23.89,92.623 25.679,95.342 L24.427,96.167 C22.915,93.871 20.374,92.5 17.63,92.5 C13.147,92.5 9.5,96.148 9.5,100.63 C9.5,104.98 12.995,108.26 17.63,108.26 L17.63,109.76" id="Fill-537" fill="#0073AA"></path>
507
+ <path d="M137.825,93.872 C137.12,87.959 132.091,83.5 126.126,83.5 C122.134,83.5 118.444,85.498 116.256,88.846 L115,88.025 C117.466,84.252 121.625,82 126.126,82 C132.851,82 138.52,87.027 139.315,93.695 L137.825,93.872" id="Fill-538" fill="#0073AA"></path>
508
+ <path d="M135.5,100.593 L134,100.593 C134,95.854 137.855,92 142.593,92 L142.593,93.5 C138.682,93.5 135.5,96.682 135.5,100.593" id="Fill-539" fill="#0073AA"></path>
509
+ <path d="M154.428955,108.394775 C161.000955,108.394775 167.34,101.993 167.34,95.42 C167.34,88.847 161.992,83.5 155.42,83.5 C149.87,83.5 145.101,87.266 143.821,92.658 C143.609,93.556 143.5,94.485 143.5,95.42 L142,95.42 C142,94.369 142.122,93.323 142.362,92.311 C143.803,86.24 149.172,82 155.42,82 C162.82,82 168.84,88.02 168.84,95.42 C168.84,102.82 162.82,109.290283 155.42,109.290283 L154.428955,108.394775 Z" id="Fill-540" fill="#0073AA"></path>
510
+ <path d="M118.996,102.234 C118.996,106.229 113.848,109.468 107.498,109.468 C101.148,109.468 96,106.229 96,102.234 C96,98.239 101.148,95 107.498,95 C113.848,95 118.996,98.239 118.996,102.234" id="Fill-541" fill="#FFFFFF"></path>
511
+ <path d="M118.468,99.233 C118.468,103.229 115.23,106.467 111.234,106.467 C107.239,106.467 104,103.229 104,99.233 C104,95.238 107.239,92 111.234,92 C115.23,92 118.468,95.238 118.468,99.233" id="Fill-542" fill="#FFFFFF"></path>
512
+ <path d="M108.565,100.283 C108.565,105.409 104.409,109.565 99.283,109.565 C94.156,109.565 90,105.409 90,100.283 C90,95.156 94.156,91 99.283,91 C104.409,91 108.565,95.156 108.565,100.283" id="Fill-543" fill="#FFFFFF"></path>
513
+ <path d="M105.5,98.985 L104,98.985 C104,94.582 107.581,91 111.983,91 C113.608,91 115.172,91.486 116.507,92.405 L115.656,93.64 C114.573,92.894 113.303,92.5 111.983,92.5 C108.408,92.5 105.5,95.409 105.5,98.985" id="Fill-544" fill="#0073AA"></path>
514
+ <path d="M154.109,103.905 L152.927,102.981 C153.429,102.339 153.72,101.568 153.769,100.751 C153.903,98.539 152.211,96.631 150,96.497 L150.09,95 C153.127,95.183 155.449,97.804 155.267,100.841 C155.198,101.963 154.798,103.022 154.109,103.905" id="Fill-545" fill="#0073AA"></path>
515
+ <path d="M89,100.033 C89,94.5 93.501,90 99.033,90 C102.414,90 105.546,91.686 107.41,94.511 L106.158,95.337 C104.572,92.934 101.909,91.5 99.033,91.5 C94.328,91.5 90.5,95.328 90.5,100.033 C90.5,104.738 92.9743652,108.234863 98.5073242,108.234863 L99.2976074,109.354492 C93.7656074,109.354492 89,105.565 89,100.033 Z" id="Fill-546" fill="#0073AA"></path>
516
+ <polygon id="Fill-547" points="0 109.5 2.739 109.5 2.739 108 0 108"></polygon>
517
+ <polygon id="Fill-548" fill="#0073AA" points="168 109.5 174.049 109.5 174.049 108 168 108"></polygon>
518
+ <polygon id="Fill-549" fill="#0073AA" points="3 109.842041 166.823 109.5 166.823 108 3 108.284424"></polygon>
519
+ <polygon id="Path" fill="#0073AA" points="69 49.5 70.398 49.5 70.398 48 69 48"></polygon>
520
+ <polygon id="Path" fill="#0073AA" points="73 49.5 74.399 49.5 74.399 48 73 48"></polygon>
521
+ <polygon id="Path" fill="#0073AA" points="77 49.5 78.398 49.5 78.398 48 77 48"></polygon>
522
+ <path d="M113.541,26.747 L112.949,28.113 L114.125,28.113 L113.541,26.747 Z M114.96,30.049 L114.522,29.034 L112.552,29.034 L112.115,30.049 L111,30.049 L113.03,25.349 L114.044,25.349 L116.076,30.049 L114.96,30.049 L114.96,30.049 Z" id="Fill-375" fill="#0073AA"></path>
523
+ <path d="M118.049,29.161 L118.768,29.161 C119.068,29.161 119.294,29.124 119.444,29.051 C119.594,28.977 119.669,28.831 119.669,28.614 C119.669,28.396 119.59,28.253 119.43,28.183 C119.271,28.114 119.017,28.079 118.668,28.079 L118.049,28.079 L118.049,29.161 Z M118.049,27.252 L118.547,27.252 C118.838,27.252 119.053,27.221 119.195,27.158 C119.337,27.095 119.407,26.959 119.407,26.751 C119.407,26.543 119.341,26.405 119.212,26.338 C119.082,26.271 118.862,26.237 118.553,26.237 L118.049,26.237 L118.049,27.252 Z M119.057,30.049 L117,30.049 L117,25.349 L118.829,25.349 C119.147,25.349 119.422,25.387 119.652,25.463 C119.884,25.54 120.056,25.643 120.174,25.773 C120.384,26.015 120.489,26.288 120.489,26.593 C120.489,26.961 120.371,27.235 120.133,27.413 L119.965,27.532 L119.797,27.609 C120.088,27.671 120.32,27.802 120.493,28.002 C120.665,28.202 120.752,28.449 120.752,28.745 C120.752,29.072 120.639,29.361 120.416,29.612 C120.155,29.904 119.703,30.049 119.057,30.049 L119.057,30.049 Z" id="Fill-376" fill="#0073AA"></path>
524
+ <path d="M123.441,29.228 C123.956,29.228 124.377,29.024 124.705,28.616 L125.377,29.309 C124.844,29.91 124.215,30.21 123.491,30.21 C122.767,30.21 122.171,29.981 121.702,29.524 C121.235,29.067 121,28.49 121,27.793 C121,27.096 121.238,26.515 121.716,26.048 C122.194,25.582 122.778,25.349 123.468,25.349 C124.238,25.349 124.884,25.643 125.405,26.229 L124.752,26.97 C124.42,26.557 124.008,26.351 123.515,26.351 C123.12,26.351 122.784,26.479 122.503,26.737 C122.223,26.995 122.082,27.343 122.082,27.779 C122.082,28.217 122.215,28.567 122.48,28.832 C122.743,29.097 123.065,29.228 123.441,29.228" id="Fill-377" fill="#0073AA"></path>
525
+ <polygon id="Fill-378" fill="#0073AA" points="98 25.849 107.862 25.849 107.862 24.349 98 24.349"></polygon>
526
+ <polygon id="Fill-379" fill="#0073AA" points="94 28.849 108.16 28.849 108.16 27.349 94 27.349"></polygon>
527
+ <polygon id="Fill-380" fill="#0073AA" points="90 31.849 107.974 31.849 107.974 30.349 90 30.349"></polygon>
528
+ <g id="Fill-455" transform="translate(31.435000, 49.500000)">
529
+ <path d="M21.582,12.02 C21.582,12.019 21.582,12.016 21.582,12.014 C21.582,12.016 21.582,12.018 21.582,12.02 M21.582,11.98 C21.582,11.978 21.581,11.976 21.581,11.973 C21.581,11.976 21.582,11.978 21.582,11.98 M21.581,11.895 C21.581,11.892 21.581,11.889 21.581,11.886 C21.581,11.889 21.581,11.892 21.581,11.895 M21.581,11.856 C21.581,11.853 21.581,11.848 21.581,11.844 C21.581,11.849 21.581,11.852 21.581,11.856 M21.58,11.817 C21.58,11.813 21.58,11.81 21.58,11.806 C21.58,11.81 21.58,11.813 21.58,11.817 M21.58,11.767 C21.58,11.757 21.58,11.747 21.58,11.737 C21.58,11.747 21.58,11.757 21.58,11.767" fill="#98C7E1"></path>
530
+ <path d="M31.179,28.396 L31.248,28.291 C31.253,28.295 31.259,28.298 31.265,28.302 C31.236,28.333 31.208,28.365 31.179,28.396" id="Fill-459" fill="#0073AA"></path>
531
+ <path d="M35.184,9.973 L32.05,16.551 C32.343,18.381 32.275,20.299 31.764,22.206 C29.821,29.455 22.37,33.756 15.122,31.813 C7.873,29.87 3.572,22.419 5.515,15.17 C7.458,7.922 14.909,3.621 22.157,5.564 C24.717,6.25 26.898,7.632 28.577,9.441 L35.184,9.973" id="Fill-460" fill="#8AC4E6"></path>
532
+ <path d="M18.619,5.845 C16.394,5.845 14.2,6.428 12.221,7.57 C9.252,9.285 7.128,12.053 6.24,15.365 C5.352,18.677 5.807,22.136 7.521,25.106 C9.236,28.076 12.004,30.2 15.316,31.088 C18.629,31.977 22.088,31.521 25.057,29.807 C28.027,28.092 30.151,25.324 31.039,22.012 C31.507,20.268 31.598,18.471 31.31,16.669 L31.273,16.439 L34.039,10.633 L28.227,10.165 L28.028,9.951 C26.381,8.177 24.283,6.91 21.963,6.288 C20.857,5.992 19.734,5.845 18.619,5.845 Z M18.663,33.032 C17.417,33.032 16.163,32.868 14.927,32.537 C11.228,31.545 8.136,29.173 6.222,25.856 C4.307,22.539 3.799,18.675 4.791,14.976 C5.782,11.277 8.155,8.185 11.471,6.271 C14.788,4.356 18.65,3.848 22.352,4.839 C24.85,5.509 27.116,6.847 28.924,8.716 L36.33,9.313 L32.827,16.666 C33.104,18.599 32.991,20.527 32.488,22.401 C31.497,26.1 29.124,29.191 25.807,31.105 C23.598,32.381 21.147,33.032 18.663,33.032 L18.663,33.032 Z" id="Fill-461" fill="#0073AA"></path>
533
+ <path d="M27.49,21.061 C28.76,16.322 25.857,11.426 21.006,10.125 C16.154,8.825 11.191,11.612 9.921,16.351 C8.651,21.09 11.554,25.986 16.405,27.287 C21.257,28.587 26.22,25.8 27.49,21.061" id="Fill-462" fill="#FFFFFF"></path>
534
+ <polyline id="Fill-463" fill="#0073AA" points="16.535 24.278 11.858 19.602 14.686 16.773 16.535 18.622 17.4762865 17.6807135 22.046 13.111 24.874 15.939 16.535 24.278 20.3554688 19.3138021"></polyline>
535
+ <polygon id="Fill-464" fill="#0073AA" points="18.243 3.306 19.743 3.306 19.743 0 18.243 0"></polygon>
536
+ <polyline id="Fill-465" fill="#0073AA" points="2.721 28.434 1.973 27.133 4.84 25.485 5.588 26.786 2.721 28.434"></polyline>
537
+ <polyline id="Fill-466" fill="#0073AA" points="0.749 23.964 0.362 22.515 3.556 21.664 3.943 23.113 0.749 23.964"></polyline>
538
+ <polyline id="Fill-467" fill="#0073AA" points="3.305 19.141 0 19.135 0.002 17.635 3.307 17.641 3.305 19.141"></polyline>
539
+ <polyline id="Fill-468" fill="#0073AA" points="3.717 15.139 0.526 14.277 0.917 12.829 4.108 13.69 3.717 15.139"></polyline>
540
+ <polyline id="Fill-469" fill="#0073AA" points="5.152 11.38 2.293 9.721 3.045 8.423 5.904 10.082 5.152 11.38"></polyline>
541
+ <polyline id="Fill-470" fill="#0073AA" points="7.511 8.121 5.178 5.778 6.241 4.719 8.574 7.062 7.511 8.121"></polyline>
542
+ <polyline id="Fill-471" fill="#0073AA" points="10.633 5.583 8.987 2.716 10.288 1.969 11.934 4.836 10.633 5.583"></polyline>
543
+ <polyline id="Fill-472" fill="#0073AA" points="14.307 3.94 13.459 0.745 14.908 0.36 15.756 3.556 14.307 3.94"></polyline>
544
+ </g>
545
+ <path d="M44.98,20.5 C40.855,20.5 37.5,23.855 37.5,27.979 C37.5,32.103 40.855,35.459 44.98,35.459 C49.104,35.459 52.46,32.103 52.46,27.979 C52.46,23.855 49.104,20.5 44.98,20.5 Z M44.98,36.959 C40.028,36.959 36,32.931 36,27.979 C36,23.028 40.028,19 44.98,19 C49.932,19 53.96,23.028 53.96,27.979 C53.96,32.931 49.932,36.959 44.98,36.959 L44.98,36.959 Z" id="Fill-699" fill="#0073AA"></path>
546
+ <path d="M48.272,36.215 L47,35.42 C48.243,33.432 48.928,30.825 48.928,28.078 C48.928,25.355 48.255,22.766 47.034,20.788 L48.31,20 C49.676,22.211 50.428,25.08 50.428,28.078 C50.428,31.102 49.662,33.992 48.272,36.215" id="Fill-700" fill="#0073AA"></path>
547
+ <path d="M42.158,36.216 C40.767,33.992 40,31.102 40,28.078 C40,25.084 40.753,22.215 42.119,20 L43.396,20.788 C42.174,22.769 41.5,25.358 41.5,28.078 C41.5,30.824 42.186,33.431 43.43,35.42 L42.158,36.216" id="Fill-701" fill="#0073AA"></path>
548
+ <polygon id="Fill-702" fill="#0073AA" points="44 36.459 45.5 36.459 45.5 20 44 20"></polygon>
549
+ <polygon id="Fill-703" fill="#0073AA" points="37 28.5 53.46 28.5 53.46 27 37 27"></polygon>
550
+ <polygon id="Fill-704" fill="#0073AA" points="38 24.5 52.212 24.5 52.212 23 38 23"></polygon>
551
+ <polygon id="Fill-705" fill="#0073AA" points="38 32.5 52.34 32.5 52.34 31 38 31"></polygon>
552
+ </g>
553
+ </symbol>
554
+
555
+
556
+ <symbol id="icon-child" width="199px" height="100px" viewBox="0 0 199 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
557
+ <defs></defs>
558
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
559
+ <path d="M162.238416,41.0256956 L161.782416,38.7086956 C161.569416,37.0636956 160.061416,35.9026956 158.416416,36.1156956 L123.618416,40.6336956 C121.972416,40.8476956 120.811416,42.3546956 121.025416,43.9996956 L127.394416,93.0586956 L166.852416,80.9746956 L162.238416,41.0256956" id="Fill-261" fill="#FFFFFF"></path>
560
+ <path d="M161.071,41.9176994 L160.615,39.6016994 C160.53,38.9556994 160.224,38.4246994 159.748,38.0576994 C159.27,37.6906994 158.681,37.5276994 158.082,37.6086994 L134.193,40.7096994 L134,39.2216994 L157.888,36.1196994 C159.944,35.8646994 161.828,37.3076994 162.095,39.3606994 L162.543,41.6286994 L161.071,41.9176994" id="Fill-262" fill="#0073AA"></path>
561
+ <path d="M131.89,98.5695 L169.208,98.5695 C170.252,98.5695 171.099,97.7225 171.099,96.6785 L171.099,44.9815 C171.099,43.9375 170.252,43.0905 169.208,43.0905 L131.89,43.0905 C130.846,43.0905 130,43.9375 130,44.9815 L130,96.6785 C130,97.7225 130.846,98.5695 131.89,98.5695" id="Fill-263" fill="#8AC4E6"></path>
562
+ <path d="M167.689,99.0695 L167.689,97.5695 C169.105,97.5695 170.256,96.4165 170.256,95.0005 L170.256,46.1595 C170.256,44.7435 169.105,43.5905 167.689,43.5905 L142,43.5905 L142,42.0905 L167.689,42.0905 C169.932,42.0905 171.756,43.9165 171.756,46.1595 L171.756,95.0005 C171.756,97.2445 169.932,99.0695 167.689,99.0695" id="Fill-264" fill="#0073AA"></path>
563
+ <polygon id="Fill-265" fill="#0073AA" points="168 49.5905 175.601 49.5905 175.601 48.0905 168 48.0905"></polygon>
564
+ <polygon id="Fill-266" fill="#0073AA" points="168 55.5905 175.601 55.5905 175.601 54.0905 168 54.0905"></polygon>
565
+ <polygon id="Fill-267" fill="#0073AA" points="168 60.5905 175.601 60.5905 175.601 59.0905 168 59.0905"></polygon>
566
+ <polygon id="Fill-268" fill="#0073AA" points="168 65.5905 175.601 65.5905 175.601 64.0905 168 64.0905"></polygon>
567
+ <polygon id="Fill-269" fill="#0073AA" points="168 71.5905 175.601 71.5905 175.601 70.0905 168 70.0905"></polygon>
568
+ <polygon id="Fill-270" fill="#0073AA" points="168 76.5905 175.601 76.5905 175.601 75.0905 168 75.0905"></polygon>
569
+ <polygon id="Fill-271" fill="#0073AA" points="168 82.5905 175.391 82.5905 175.391 81.0905 168 81.0905"></polygon>
570
+ <polygon id="Fill-272" fill="#0073AA" points="168 87.5905 175.391 87.5905 175.391 86.0905 168 86.0905"></polygon>
571
+ <polygon id="Fill-273" fill="#0073AA" points="168 92.5905 175.391 92.5905 175.391 91.0905 168 91.0905"></polygon>
572
+ <path d="M142,65.0595 C142,70.0125 146.016,74.0285 150.969,74.0285 C155.922,74.0285 159.938,70.0125 159.938,65.0595 C159.938,60.1065 155.922,56.0905 150.969,56.0905 C146.016,56.0905 142,60.1065 142,65.0595" id="Fill-274" fill="#FFFFFF"></path>
573
+ <path d="M150.718,56.5905 C146.186,56.5905 142.5,60.2775 142.5,64.8095 C142.5,69.3415 146.186,73.0285 150.718,73.0285 C155.25,73.0285 158.937,69.3415 158.937,64.8095 C158.937,60.2775 155.25,56.5905 150.718,56.5905 Z M150.718,74.5285 C145.359,74.5285 141,70.1695 141,64.8095 C141,59.4505 145.359,55.0905 150.718,55.0905 C156.078,55.0905 160.437,59.4505 160.437,64.8095 C160.437,70.1695 156.078,74.5285 150.718,74.5285 L150.718,74.5285 Z" id="Fill-275" fill="#0073AA"></path>
574
+ <path d="M43.984,22.8825 L43.984,97.5515 C43.984,97.9885 43.63,98.3435 43.193,98.3435 L32.792,98.3435 C32.354,98.3435 32,97.9885 32,97.5515 L32,22.8825 C32,22.4455 32.354,22.0905 32.792,22.0905 L43.193,22.0905 C43.63,22.0905 43.984,22.4455 43.984,22.8825" id="Fill-276" fill="#8AC4E6"></path>
575
+ <path d="M32.687,98.3435 L36.932,98.3435 L36.932,22.0905 L32.687,22.0905 C32.308,22.0905 32,22.4355 32,22.8605 L32,97.5745 C32,97.9995 32.308,98.3435 32.687,98.3435" id="Fill-277" fill="#E7F7FF"></path>
576
+ <path d="M32.5,98.2565 L31,98.2565 L31,23.6765 C31,22.8025 31.712,22.0905 32.586,22.0905 L42.898,22.0905 C43.773,22.0905 44.485,22.8025 44.485,23.6765 L44.485,29.9295 L42.985,29.9295 L42.985,23.6765 L42.898,23.5905 L32.586,23.5905 L32.5,23.6765 L32.5,98.2565" id="Fill-278" fill="#0073AA"></path>
577
+ <polygon id="Fill-279" fill="#0073AA" points="32 93.5905 37.519 93.5905 37.519 92.0905 32 92.0905"></polygon>
578
+ <polygon id="Fill-280" fill="#0073AA" points="32 85.5905 37.519 85.5905 37.519 84.0905 32 84.0905"></polygon>
579
+ <polygon id="Fill-281" fill="#0073AA" points="32 77.5905 37.519 77.5905 37.519 76.0905 32 76.0905"></polygon>
580
+ <polygon id="Fill-282" fill="#0073AA" points="32 69.5905 37.519 69.5905 37.519 68.0905 32 68.0905"></polygon>
581
+ <polygon id="Fill-283" fill="#0073AA" points="32 61.5905 37.519 61.5905 37.519 60.0905 32 60.0905"></polygon>
582
+ <polygon id="Fill-284" fill="#0073AA" points="32 53.5905 37.519 53.5905 37.519 52.0905 32 52.0905"></polygon>
583
+ <polygon id="Fill-285" fill="#0073AA" points="32 45.5905 37.519 45.5905 37.519 44.0905 32 44.0905"></polygon>
584
+ <polygon id="Fill-286" fill="#0073AA" points="32 37.5905 37.519 37.5905 37.519 36.0905 32 36.0905"></polygon>
585
+ <polygon id="Fill-287" fill="#0073AA" points="32 29.5905 37.519 29.5905 37.519 28.0905 32 28.0905"></polygon>
586
+ <path d="M183.024,39.5905 L178.75,39.5905 C178.336,39.5905 178,39.2545 178,38.8405 C178,38.4265 178.336,38.0905 178.75,38.0905 L183.024,38.0905 C183.438,38.0905 183.774,38.4265 183.774,38.8405 C183.774,39.2545 183.438,39.5905 183.024,39.5905" id="Fill-288" fill="#0073AA"></path>
587
+ <path d="M180.75,41.8645 C180.336,41.8645 180,41.5285 180,41.1145 L180,36.8405 C180,36.4255 180.336,36.0905 180.75,36.0905 C181.164,36.0905 181.5,36.4255 181.5,36.8405 L181.5,41.1145 C181.5,41.5285 181.164,41.8645 180.75,41.8645" id="Fill-289" fill="#0073AA"></path>
588
+ <path d="M77.024,20.5905 L72.75,20.5905 C72.336,20.5905 72,20.2545 72,19.8405 C72,19.4265 72.336,19.0905 72.75,19.0905 L77.024,19.0905 C77.438,19.0905 77.774,19.4265 77.774,19.8405 C77.774,20.2545 77.438,20.5905 77.024,20.5905" id="Fill-290" fill="#0073AA"></path>
589
+ <path d="M74.75,21.8645 C74.336,21.8645 74,21.5285 74,21.1145 L74,16.8405 C74,16.4265 74.336,16.0905 74.75,16.0905 C75.164,16.0905 75.5,16.4265 75.5,16.8405 L75.5,21.1145 C75.5,21.5285 75.164,21.8645 74.75,21.8645" id="Fill-291" fill="#0073AA"></path>
590
+ <path d="M136.74975,9.61225064 C136.55775,9.61225064 136.36575,9.53925064 136.21975,9.39225064 C135.92675,9.09925064 135.92675,8.62525064 136.21975,8.33225064 L139.24175,5.31025064 C139.53575,5.01625064 140.01075,5.01825064 140.30275,5.31025064 C140.59575,5.60325064 140.59575,6.07825064 140.30275,6.37125064 L137.28075,9.39225064 C137.13375,9.53925064 136.94275,9.61225064 136.74975,9.61225064" id="Fill-292" fill="#0073AA"></path>
591
+ <path d="M139.77275,9.61225 C139.57975,9.61225 139.38875,9.53925 139.24175,9.39225 L136.21975,6.37125 C135.92675,6.07825 135.92675,5.60325 136.21975,5.31025 C136.51275,5.01725 136.98775,5.01725 137.28075,5.31025 L140.30275,8.33225 C140.59575,8.62525 140.59575,9.09925 140.30275,9.39225 C140.15575,9.53925 139.96475,9.61225 139.77275,9.61225" id="Fill-293" fill="#0073AA"></path>
592
+ <path d="M1.74975,35.61325 C1.55775,35.61325 1.36575,35.54025 1.21975,35.39325 C0.92675,35.10025 0.92675,34.62625 1.21975,34.33325 L4.24175,31.31025 C4.53475,31.01725 5.00975,31.01725 5.30275,31.31025 C5.59575,31.60325 5.59575,32.07825 5.30275,32.37125 L2.27975,35.39325 C2.13375,35.54025 1.94275,35.61325 1.74975,35.61325" id="Fill-294" fill="#0073AA"></path>
593
+ <path d="M4.77275,35.61325 C4.57975,35.61325 4.38875,35.54025 4.24175,35.39325 L1.21975,32.37125 C0.92675,32.07825 0.92675,31.60325 1.21975,31.31025 C1.51275,31.01725 1.98675,31.01725 2.27975,31.31025 L5.30275,34.33325 C5.59575,34.62625 5.59575,35.10025 5.30275,35.39325 C5.15575,35.54025 4.96475,35.61325 4.77275,35.61325" id="Fill-295" fill="#0073AA"></path>
594
+ <polygon id="Fill-296" fill="#0073AA" points="60 1.5905 89.39 1.5905 89.39 0.0905 60 0.0905"></polygon>
595
+ <polygon id="Fill-297" fill="#0073AA" points="112 23.5905 141.39 23.5905 141.39 22.0905 112 22.0905"></polygon>
596
+ <polygon id="Fill-298" fill="#0073AA" points="76 7.5905 130.342 7.5905 130.342 6.0905 76 6.0905"></polygon>
597
+ <polygon id="Fill-299" fill="#0073AA" points="0.557 45.5905 16.48 45.5905 16.48 44.0905 0.557 44.0905"></polygon>
598
+ <polygon id="Fill-300" fill="#0073AA" points="179 46.5905 198.352 46.5905 198.352 45.0905 179 45.0905"></polygon>
599
+ <polygon id="Fill-301" fill="#0073AA" points="91 1.5905 94.57 1.5905 94.57 0.0905 91 0.0905"></polygon>
600
+ <path d="M105.693,31.4375 C105.693,32.1815 105.09,32.7845 104.347,32.7845 C103.603,32.7845 103,32.1815 103,31.4375 C103,30.6935 103.603,30.0905 104.347,30.0905 C105.09,30.0905 105.693,30.6935 105.693,31.4375" id="Fill-302" fill="#0073AA"></path>
601
+ <path d="M17.52,67.8505 C17.52,68.8215 16.732,69.6095 15.76,69.6095 C14.788,69.6095 14,68.8215 14,67.8505 C14,66.8785 14.788,66.0905 15.76,66.0905 C16.732,66.0905 17.52,66.8785 17.52,67.8505" id="Fill-303" fill="#0073AA"></path>
602
+ <path d="M39.52,14.8495 C39.52,15.8215 38.732,16.6095 37.76,16.6095 C36.788,16.6095 36,15.8215 36,14.8495 C36,13.8785 36.788,13.0905 37.76,13.0905 C38.732,13.0905 39.52,13.8785 39.52,14.8495" id="Fill-304" fill="#0073AA"></path>
603
+ <path d="M184.519,82.8505 C184.519,83.8225 183.731,84.6105 182.759,84.6105 C181.788,84.6105 181,83.8225 181,82.8505 C181,81.8785 181.788,81.0905 182.759,81.0905 C183.731,81.0905 184.519,81.8785 184.519,82.8505" id="Fill-305" fill="#0073AA"></path>
604
+ <path d="M132.248,98.2755 L131.95,45.3365 C131.95,41.3345 128.303,38.0905 123.804,38.0905 L48,38.0905 L48.298,98.3365 L132.248,98.2755" id="Fill-306" fill="#FFFFFF"></path>
605
+ <path d="M131.498,99.0295 L131.2,46.0905 C131.2,42.5045 127.882,39.5905 123.803,39.5905 L48,39.5905 L48,38.0905 L123.803,38.0905 C128.709,38.0905 132.7,41.6775 132.7,46.0865 L132.998,99.0215 L131.498,99.0295" id="Fill-307" fill="#0073AA"></path>
606
+ <path d="M124,98.4195 L132.457,98.4195 L132.457,35.9435 C132.457,32.7115 129.551,30.0905 125.966,30.0905 L124,30.0905 L124,98.4195" id="Fill-308" fill="#0073AA"></path>
607
+ <path d="M124.5,99.1685 L123,99.1685 L123,30.0905 L125.716,30.0905 C129.709,30.0905 132.957,33.3385 132.957,37.3305 L132.957,45.1685 L131.457,45.1685 L131.457,37.3305 C131.457,34.1655 128.881,31.5905 125.716,31.5905 L124.5,31.5905 L124.5,99.1685" id="Fill-309" fill="#0073AA"></path>
608
+ <path d="M50.457,98.4185 L42,98.4185 L42,35.9425 C42,32.7105 44.906,30.0905 48.49,30.0905 L50.457,30.0905 L50.457,98.4185" id="Fill-310" fill="#0073AA"></path>
609
+ <path d="M51.957,99.1685 L50.457,99.1685 L50.457,31.5905 L49.24,31.5905 C46.075,31.5905 43.5,34.1655 43.5,37.3305 L43.5,99.1685 L42,99.1685 L42,37.3305 C42,33.3385 45.248,30.0905 49.24,30.0905 L51.957,30.0905 L51.957,99.1685" id="Fill-311" fill="#0073AA"></path>
610
+ <path d="M102.63975,55.48925 C108.50475,61.35425 108.50475,70.86425 102.63975,76.73025 C96.77375,82.59525 87.26475,82.59525 81.39875,76.73025 C75.53375,70.86425 75.53375,61.35425 81.39875,55.48925 C87.26475,49.62425 96.77375,49.62425 102.63975,55.48925" id="Fill-312" fill="#E7F7FF"></path>
611
+ <path d="M86.597,59.7075 C85.994,59.6295 85.379,59.5905 84.77,59.5905 L84.733,58.0905 L84.77,58.0905 C85.443,58.0905 86.123,58.1345 86.789,58.2185 L86.597,59.7075 M81.042,60.0845 L80.652,58.6345 C81.316,58.4565 81.996,58.3205 82.674,58.2305 L82.871,59.7165 C82.258,59.7985 81.643,59.9215 81.042,60.0845 M90.198,60.6585 C89.625,60.4235 89.03,60.2235 88.427,60.0635 L88.812,58.6145 C89.477,58.7905 90.134,59.0105 90.766,59.2695 L90.198,60.6585 M77.605,61.5145 L76.851,60.2175 C77.444,59.8735 78.066,59.5645 78.7,59.3015 L79.276,60.6855 C78.703,60.9235 78.141,61.2025 77.605,61.5145 M93.423,62.5105 C92.932,62.1345 92.411,61.7875 91.871,61.4775 L92.62,60.1765 C93.214,60.5185 93.792,60.9035 94.335,61.3195 L93.423,62.5105 M74.68,63.7685 L73.595,62.7325 L73.62,62.7085 C74.094,62.2325 74.606,61.7825 75.139,61.3705 L76.057,62.5555 C75.574,62.9295 75.111,63.3385 74.68,63.7685 M96.092,65.1715 C95.717,64.6835 95.302,64.2115 94.86,63.7685 L95.889,62.6765 L95.92,62.7085 C96.409,63.1965 96.867,63.7175 97.282,64.2575 L96.092,65.1715 M72.395,66.7495 L71.094,66.0015 C71.435,65.4085 71.82,64.8315 72.239,64.2865 L73.428,65.1985 C73.05,65.6915 72.703,66.2135 72.395,66.7495 M97.955,68.3955 C97.719,67.8235 97.441,67.2595 97.13,66.7215 L98.429,65.9715 C98.773,66.5665 99.079,67.1885 99.342,67.8215 L97.955,68.3955 M70.977,70.1915 L69.528,69.8065 C69.703,69.1455 69.923,68.4875 70.185,67.8525 L71.572,68.4235 C71.336,68.9975 71.136,69.5915 70.977,70.1915 M98.918,71.9915 C98.837,71.3735 98.715,70.7585 98.555,70.1625 L100.004,69.7735 C100.181,70.4335 100.316,71.1135 100.405,71.7965 L98.918,71.9915 M69,73.8865 L69,73.8595 C69,73.1815 69.044,72.4985 69.13,71.8305 L70.619,72.0215 C70.54,72.6275 70.5,73.2455 70.5,73.8595 L69,73.8865 M100.408,75.9125 L98.919,75.7175 C98.999,75.1055 99.041,74.4805 99.041,73.8585 L100.54,73.8545 L100.54,73.8585 C100.54,74.5455 100.496,75.2355 100.408,75.9125 M69.542,77.9665 C69.365,77.3065 69.228,76.6275 69.138,75.9445 L70.624,75.7475 C70.706,76.3645 70.83,76.9805 70.991,77.5765 L69.542,77.9665 M99.347,79.8865 L97.96,79.3165 C98.198,78.7395 98.399,78.1445 98.558,77.5485 L100.007,77.9345 C99.831,78.5925 99.61,79.2505 99.347,79.8865 M71.124,81.7665 C70.778,81.1705 70.469,80.5475 70.207,79.9185 L71.592,79.3415 C71.829,79.9125 72.108,80.4745 72.42,81.0145 L71.124,81.7665 M97.289,83.4515 L96.099,82.5375 C96.48,82.0425 96.829,81.5205 97.136,80.9875 L98.435,81.7375 C98.095,82.3275 97.709,82.9035 97.289,83.4515 M73.636,85.0265 L73.62,85.0095 C73.139,84.5295 72.687,84.0155 72.274,83.4805 L73.461,82.5645 C73.835,83.0475 74.246,83.5135 74.68,83.9495 L73.636,85.0265 M94.382,86.3625 L93.466,85.1745 C93.955,84.7975 94.423,84.3855 94.86,83.9495 L95.929,85.0015 L95.92,85.0095 C95.439,85.4915 94.921,85.9465 94.382,86.3625 M76.902,87.5295 C76.307,87.1865 75.731,86.8015 75.188,86.3845 L76.1,85.1955 C76.591,85.5715 77.114,85.9205 77.652,86.2305 L76.902,87.5295 M90.818,88.4265 L90.244,87.0425 C90.817,86.8035 91.379,86.5255 91.916,86.2155 L92.668,87.5115 C92.075,87.8565 91.453,88.1645 90.818,88.4265 M80.706,89.0985 C80.04,88.9215 79.382,88.6995 78.751,88.4395 L79.324,87.0525 C79.894,87.2885 80.489,87.4885 81.092,87.6495 L80.706,89.0985 M86.843,89.4925 L86.646,88.0055 C87.262,87.9235 87.878,87.8015 88.477,87.6405 L88.866,89.0905 C88.204,89.2665 87.523,89.4025 86.843,89.4925 M84.786,89.6295 L84.77,89.6295 C84.089,89.6295 83.403,89.5845 82.73,89.4975 L82.923,88.0085 C83.533,88.0885 84.155,88.1295 84.77,88.1295 L84.786,89.6295" id="Fill-313" fill="#0073AA"></path>
612
+ <polygon id="Fill-314" fill="#0073AA" points="13 99.5905 176.821 99.5905 176.821 98.0905 13 98.0905"></polygon>
613
+ <polygon id="Fill-315" fill="#0073AA" points="8 99.5905 10.739 99.5905 10.739 98.0905 8 98.0905"></polygon>
614
+ <polygon id="Fill-316" fill="#0073AA" points="178 99.5905 184.05 99.5905 184.05 98.0905 178 98.0905"></polygon>
615
+ <path d="M123.193,97.8505 L79,97.8505 C79.009,97.4835 79.156,97.1265 79.427,96.8555 L87.587,88.6955 C88.03,88.6165 88.47,88.5185 88.902,88.4035 L88.685,87.5975 L95.741,80.5415 C96.952,80.2295 98.133,79.7635 99.25,79.1455 L99.383,79.1995 C99.406,79.1435 99.429,79.0885 99.451,79.0315 C100.578,78.3855 101.639,77.5805 102.601,76.6185 C104.569,74.6505 105.876,72.2725 106.524,69.7595 L123.193,53.0905 L123.193,66.3225 L99.522,89.9935 L123.193,89.9935 L123.193,97.8505 M91.952,85.5285 C91.415,85.8385 90.853,86.1165 90.28,86.3555 L90.854,87.7395 C91.489,87.4775 92.111,87.1695 92.704,86.8245 L91.952,85.5285 M94.896,83.2625 C94.459,83.6985 93.991,84.1105 93.502,84.4875 L94.418,85.6755 C94.957,85.2595 95.475,84.8045 95.956,84.3225 L95.965,84.3145 L94.896,83.2625 M97.172,80.3005 C96.865,80.8335 96.516,81.3555 96.135,81.8505 L97.325,82.7645 C97.745,82.2165 98.131,81.6405 98.471,81.0505 L97.172,80.3005" id="Fill-317" fill="#B4D7E7"></path>
616
+ <polyline id="Fill-318" fill="#0073AA" points="133.246 97.7965 133 97.7965 133 54.0905 133 54.0945 133.246 97.7965"></polyline>
617
+ <polyline id="Fill-319" fill="#4B97C6" points="132.707 98.3405 125 98.3405 125 90.4835 129.48 90.4835 129.48 60.8315 125 65.3125 125 52.0805 131.99 45.0905 132.707 45.0905 132.707 98.3405"></polyline>
618
+ <path d="M123,97.8075 L124.5,97.8075 L124.5,89.9505 L123,89.9505 L123,97.8075 Z M123,66.2795 L123,53.0475 L124.5,51.5475 L124.5,64.7795 L123,66.2795 L123,66.2795 Z M132.957,44.5575 L131.49,44.5575 L132.957,43.0905 L132.957,44.5575 L132.957,44.5575 Z" id="Fill-320" fill="#0073AA"></path>
619
+ <path d="M96,80.8725 L98.472,78.4005 C98.404,78.5885 98.332,78.7755 98.255,78.9605 L99.509,79.4765 C98.392,80.0945 97.211,80.5605 96,80.8725 M99.71,79.3625 C99.943,78.7785 100.142,78.1795 100.302,77.5785 L99.506,77.3665 L106.783,70.0905 C106.135,72.6035 104.828,74.9815 102.86,76.9495 C101.898,77.9115 100.837,78.7165 99.71,79.3625" id="Fill-321" fill="#93C5E0"></path>
620
+ <path d="M88,88.7505 L89.098,87.6525 L89.315,88.4585 C88.883,88.5735 88.443,88.6715 88,88.7505 M91.267,87.7945 L90.693,86.4105 C91.266,86.1715 91.828,85.8935 92.365,85.5835 L93.117,86.8795 C92.524,87.2245 91.902,87.5325 91.267,87.7945 M94.831,85.7305 L93.915,84.5425 C94.404,84.1655 94.872,83.7535 95.309,83.3175 L96.378,84.3695 L96.369,84.3775 C95.888,84.8595 95.37,85.3145 94.831,85.7305 M97.738,82.8195 L96.548,81.9055 C96.929,81.4105 97.278,80.8885 97.585,80.3555 L98.884,81.1055 C98.544,81.6955 98.158,82.2715 97.738,82.8195 M99.796,79.2545 L99.663,79.2005 L98.409,78.6845 C98.486,78.4995 98.558,78.3125 98.626,78.1245 L99.66,77.0905 L100.456,77.3025 C100.296,77.9035 100.097,78.5025 99.864,79.0865 C99.842,79.1435 99.819,79.1985 99.796,79.2545" id="Fill-322" fill="#0073AA"></path>
621
+ <path d="M132.400456,98.8405 L123.193456,98.8405 L123.193456,98.7865 L79.1894564,98.8175 L79.1124564,98.6885 C79.0314564,98.4945 78.9954564,98.2915 79.0004564,98.0905 L132.646456,98.0905 L132.650456,98.7755 L132.400456,98.7775 L132.400456,98.8405" id="Fill-323" fill="#0073AA"></path>
622
+ <path d="M99.9627403,91.2652403 L132.17474,91.2652403 L132.17474,59.0532403 L99.9627403,91.2652403 Z M140.03174,99.1222403 L82.7137403,99.1222403 C82.0787403,99.1222403 81.7617403,98.3552403 82.2097403,97.9062403 L138.81674,41.3002403 C139.26474,40.8522403 140.03174,41.1692403 140.03174,41.8042403 L140.03174,99.1222403 L140.03174,99.1222403 Z" id="Fill-324" fill="#E7F7FF"></path>
623
+ <path d="M102.522778,90.2654277 L132.173778,90.2654277 L132.173778,60.6134277 L102.522778,90.2654277 Z M133.673778,91.7654277 L98.901778,91.7654277 L133.673778,56.9924277 L133.673778,91.7654277 Z M140.031778,41.5534277 L83.489778,98.1864277 L140.031778,98.1234277 L140.031778,41.5534277 Z M141.531778,99.6224277 L83.462778,99.6224277 C82.868778,99.6224277 82.339778,99.2684277 82.112778,98.7204277 C81.884778,98.1724277 82.008778,97.5474277 82.427778,97.1274277 L139.034778,40.5204277 C139.453778,40.1004277 140.079778,39.9744277 140.627778,40.2034277 C141.176778,40.4294277 141.531778,40.9604277 141.531778,41.5534277 L141.531778,99.6224277 L141.531778,99.6224277 Z" id="Fill-325" fill="#0073AA"></path>
624
+ <polygon id="Fill-326" fill="#0073AA" points="90 98.9135 91.5 98.9135 91.5 95.0905 90 95.0905"></polygon>
625
+ <polygon id="Fill-327" fill="#0073AA" points="96 98.9135 97.5 98.9135 97.5 95.0905 96 95.0905"></polygon>
626
+ <polygon id="Fill-328" fill="#0073AA" points="103 98.9135 104.5 98.9135 104.5 95.0905 103 95.0905"></polygon>
627
+ <polygon id="Fill-329" fill="#0073AA" points="109 98.9135 110.5 98.9135 110.5 95.0905 109 95.0905"></polygon>
628
+ <polygon id="Fill-330" fill="#0073AA" points="115 98.9135 116.5 98.9135 116.5 95.0905 115 95.0905"></polygon>
629
+ <polygon id="Fill-331" fill="#0073AA" points="121 98.9135 122.5 98.9135 122.5 95.0905 121 95.0905"></polygon>
630
+ <polygon id="Fill-332" fill="#0073AA" points="128 98.9135 129.5 98.9135 129.5 95.0905 128 95.0905"></polygon>
631
+ <polygon id="Fill-333" fill="#0073AA" points="134 98.9135 135.5 98.9135 135.5 95.0905 134 95.0905"></polygon>
632
+ <polygon id="Fill-334" fill="#0073AA" points="137 92.5905 140.823 92.5905 140.823 91.0905 137 91.0905"></polygon>
633
+ <polygon id="Fill-335" fill="#0073AA" points="137 86.5905 140.823 86.5905 140.823 85.0905 137 85.0905"></polygon>
634
+ <polygon id="Fill-336" fill="#0073AA" points="137 79.5905 140.823 79.5905 140.823 78.0905 137 78.0905"></polygon>
635
+ <polygon id="Fill-337" fill="#0073AA" points="137 73.5905 140.823 73.5905 140.823 72.0905 137 72.0905"></polygon>
636
+ <polygon id="Fill-338" fill="#0073AA" points="137 67.5905 140.823 67.5905 140.823 66.0905 137 66.0905"></polygon>
637
+ <polygon id="Fill-339" fill="#0073AA" points="137 61.5905 140.823 61.5905 140.823 60.0905 137 60.0905"></polygon>
638
+ <polygon id="Fill-340" fill="#0073AA" points="137 54.5905 140.823 54.5905 140.823 53.0905 137 53.0905"></polygon>
639
+ <polygon id="Fill-341" fill="#0073AA" points="137 48.5905 140.823 48.5905 140.823 47.0905 137 47.0905"></polygon>
640
+ <path d="M22.578,98.8455 L26.813,98.8455 C27.132,98.8455 27.391,98.5865 27.391,98.2675 L27.391,37.2565 L27.338,37.0145 L25.22,32.4265 C25.013,31.9785 24.377,31.9785 24.171,32.4265 L22.053,37.0145 L22,37.2565 L22,98.2675 C22,98.5865 22.258,98.8455 22.578,98.8455" id="Fill-342" fill="#FFFFFF"></path>
641
+ <polygon id="Fill-343" fill="#62A6CF" points="22 92.8925 27.391 92.8925 27.391 39.0905 22 39.0905"></polygon>
642
+ <path d="M22.5,98.1695 L26.39,98.1695 L26.39,37.3685 L24.445,33.1535 L22.5,37.3685 L22.5,98.1695 Z M27.89,99.6695 L21,99.6695 L21,37.0385 L23.453,31.7255 C23.633,31.3345 24.014,31.0905 24.446,31.0905 C24.877,31.0905 25.258,31.3345 25.438,31.7255 L27.89,37.0385 L27.89,99.6695 L27.89,99.6695 Z" id="Fill-344" fill="#0073AA"></path>
643
+ <polygon id="Fill-345" fill="#0073AA" points="22 39.5905 27.577 39.5905 27.577 38.0905 22 38.0905"></polygon>
644
+ <polygon id="Fill-346" fill="#0073AA" points="22 93.5905 28.195 93.5905 28.195 92.0905 22 92.0905"></polygon>
645
+ </g>
646
+ </symbol>
647
+
648
+ </svg>
assets/js/importer.js CHANGED
@@ -1,123 +1,652 @@
1
- var ft_import_running = false;
 
2
  window.onbeforeunload = function() {
3
- if ( ft_import_running ) {
4
- return FT_IMPORT_DEMO.confirm_leave;
5
  }
6
  };
7
 
8
 
9
 
10
- jQuery( document ).ready( function( $ ){
 
 
11
 
12
- $( '.ft-demo-import-now').on( 'click', function( e ){
13
- e.preventDefault();
14
- var btn = $( this);
15
- if ( btn.hasClass( 'disabled' ) ) {
16
- return ;
17
- }
18
- // Make sure user want to import
19
- var c = confirm( FT_IMPORT_DEMO.confirm_import );
20
- if ( ! c ) {
21
- return;
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- btn.addClass( 'disabled' );
25
- btn.addClass( 'updating-message' );
26
-
27
- var url = btn.attr( 'href' );
28
- var frame = $( '<iframe style="display: none;"></iframe>' );
29
- frame.appendTo('body');
30
- // Thanks http://jsfiddle.net/KSXkS/1/
31
- try { // simply checking may throw in ie8 under ssl or mismatched protocol
32
- doc = frame[0].contentDocument ? frame[0].contentDocument : frame[0].document;
33
- } catch(err) {
34
- doc = frame[0].document;
 
 
 
35
  }
36
- doc.open();
37
-
38
- // Download url first
39
- url = btn.attr( 'data-download' );
40
- btn.html( FT_IMPORT_DEMO.downloading );
41
- var notice = $( '.ft-ajax-notice' );
42
- notice.html( );
43
-
44
- ft_import_running = true;
45
- $.ajax( {
46
- url: url,
47
- error: function(){
48
- var msg = $( '<div class="ft-import-box ft-import-theme"></div>' );
49
- btn.removeClass( 'updating-message' );
50
- btn.html( FT_IMPORT_DEMO.import_error );
51
- msg.addClass( 'ft-import-error').html( '<p>'+FT_IMPORT_DEMO.no_data_found+'</p>' );
52
- notice.html( msg );
53
- ft_import_running = false;
54
- console.log( 'download_error' );
55
- },
56
- success: function( res ){
57
- // btn.removeClass( 'disabled' );
58
- //btn.removeClass( 'updating-message' );
59
- //doc.close();
60
-
61
- // Import
62
- setTimeout( function(){
63
- btn.html( FT_IMPORT_DEMO.importing );
64
- url = btn.attr( 'data-import' );
65
- $.ajax( {
66
- url: url,
67
- dataType: 'html',
68
- error: function( jqXHR, status, errorThrown ){
69
-
70
- btn.removeClass( 'updating-message' );
71
- doc.close();
72
- btn.html( FT_IMPORT_DEMO.import_error );
73
- btn.removeClass( 'button-primary' );
74
- btn.addClass( 'button-secondary' );
75
- var msg = $( '<div class="ft-import-box ft-import-theme"></div>' );
76
- var err = jqXHR.statusText + ' ('+jqXHR.status+')';
77
- msg.addClass( 'ft-import-error').html( '<p>'+err+'</p>' );
78
- msg.append( '<p>'+FT_IMPORT_DEMO.import_error_msg+'</p>' );
79
- notice.html( msg );
80
-
81
- ft_import_running = false;
82
-
83
- },
84
- success: function( res ){
85
- //btn.removeClass( 'disabled' );
86
- btn.removeClass( 'updating-message' );
87
- doc.close();
88
- btn.html( FT_IMPORT_DEMO.imported );
89
- btn.removeClass( 'button-primary' );
90
- btn.addClass( 'button-secondary' );
91
-
92
- var msg = $( '<div class="ft-import-box ft-import-theme"></div>' );
93
- if ( res.indexOf( 'demo_imported' ) > -1 ) {
94
- res = res.replace(/demo_imported/i, '');
95
- msg.addClass( 'ft-import-success').html( '<p>'+FT_IMPORT_DEMO.demo_imported+'</p>' );
96
- msg.append( '<div class="import_log">'+res+'</div>' );
97
- } else if ( res.indexOf( 'no_data_found' ) > -1 ){
98
- res = res.replace(/no_data_found/i, '');
99
- msg.addClass( 'ft-import-error').html( '<p>'+FT_IMPORT_DEMO.no_data_found+'</p>' );
100
- msg.append( '<div class="import_log">'+res+'</div>' );
101
- } else if ( res.indexOf( 'demo_import_failed' ) > -1 ){
102
- msg.addClass( 'ft-import-error').html( '<p>'+FT_IMPORT_DEMO.demo_import_failed+'</p>' );
103
- } else {
104
- res = res.replace(/demo_imported/i, '');
105
- msg.addClass( 'ft-import-success').html( '<p>'+FT_IMPORT_DEMO.demo_imported+'</p>' );
106
- msg.append( '<div class="import_log">'+res+'</div>' );
107
- }
108
 
109
- notice.html( msg );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- ft_import_running = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  }
114
- } );
115
- }, 1000 );
116
 
 
 
 
 
 
 
117
  }
118
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
 
121
 
122
- } );
123
- } );
1
+ var demo_contents_import_running = false;
2
+ var demo_contents_iframe_running = false;
3
  window.onbeforeunload = function() {
4
+ if ( demo_contents_import_running ) {
5
+ return demo_contents_params.confirm_leave;
6
  }
7
  };
8
 
9
 
10
 
11
+ // -------------------------------------------------------------------------------
12
+ var demo_contents_working_plugins = window.demo_contents_working_plugins || {};
13
+ var demo_contents_viewing_theme = window.demo_contents_viewing_theme || {};
14
 
15
+ (function ( $ ) {
16
+
17
+ var demo_contents_params = demo_contents_params || window.demo_contents_params;
18
+
19
+ if( typeof demo_contents_params.plugins.activate !== "object" ) {
20
+ demo_contents_params.plugins.activate = {};
21
+ }
22
+ var $document = $( document );
23
+
24
+ /**
25
+ * Function that loads the Mustache template
26
+ */
27
+ var repeaterTemplate = _.memoize(function () {
28
+ var compiled,
29
+ /*
30
+ * Underscore's default ERB-style templates are incompatible with PHP
31
+ * when asp_tags is enabled, so WordPress uses Mustache-inspired templating syntax.
32
+ *
33
+ * @see track ticket #22344.
34
+ */
35
+ options = {
36
+ evaluate: /<#([\s\S]+?)#>/g,
37
+ interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
38
+ escape: /\{\{([^\}]+?)\}\}(?!\})/g,
39
+ variable: 'data'
40
+ };
41
 
42
+ return function (data, tplId ) {
43
+ if ( typeof tplId === "undefined" ) {
44
+ tplId = '#tmpl-demo-contents--preview';
45
+ }
46
+ compiled = _.template(jQuery( tplId ).html(), null, options);
47
+ return compiled(data);
48
+ };
49
+ });
50
+
51
+
52
+ String.prototype.format = function() {
53
+ var newStr = this, i = 0;
54
+ while (/%s/.test(newStr)) {
55
+ newStr = newStr.replace("%s", arguments[i++]);
56
  }
57
+ return newStr;
58
+ };
59
+
60
+ var template = repeaterTemplate();
61
+
62
+ var ftDemoContents = {
63
+ plugins: {
64
+ install: {},
65
+ all: {},
66
+ activate: {}
67
+ },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ loading: function(){
70
+ demo_contents_import_running = true;
71
+ demo_contents_iframe_running = null;
72
+ $( '#demo_contents_iframe_running' ).remove();
73
+ var frame = $( '<iframe id="demo_contents_iframe_running" style="display: none;"></iframe>' );
74
+ frame.appendTo('body');
75
+ var doc;
76
+ // Thanks http://jsfiddle.net/KSXkS/1/
77
+ try { // simply checking may throw in ie8 under ssl or mismatched protocol
78
+ doc = frame[0].contentDocument ? frame[0].contentDocument : frame[0].document;
79
+ } catch(err) {
80
+ doc = frame[0].document;
81
+ }
82
+ doc.open();
83
+ // doc.close();
84
+ },
85
+
86
+ end_loading: function(){
87
+ $( '#demo_contents_iframe_running' ).remove();
88
+ demo_contents_import_running = false;
89
+ },
90
 
91
+ loading_step: function( $element ){
92
+ $element.removeClass( 'demo-contents--waiting demo-contents--running' );
93
+ $element.addClass( 'demo-contents--running' );
94
+ },
95
+ completed_step: function( $element, event_trigger ){
96
+ $element.removeClass( 'demo-contents--running demo-contents--waiting' ).addClass( 'demo-contents--completed' );
97
+ if ( typeof event_trigger !== "undefined" ) {
98
+ $document.trigger( event_trigger );
99
+ }
100
+ },
101
+ preparing_plugins: function( plugins ) {
102
+ var that = this;
103
+ if ( typeof plugins === "undefined" ) {
104
+ plugins = demo_contents_params.plugins;
105
+ }
106
+ plugins = _.defaults( plugins, {
107
+ install: {},
108
+ all: {},
109
+ activate: {}
110
+ } );
111
 
112
+ demo_contents_working_plugins = plugins;
113
+ that.plugins = demo_contents_working_plugins;
114
+
115
+ var $list_install_plugins = $('.demo-contents-install-plugins');
116
+ var n = _.size(that.plugins.all);
117
+ if (n > 0) {
118
+ var $child_steps = $list_install_plugins.find('.demo-contents--child-steps');
119
+ $.each( that.plugins.all, function ($slug, plugin) {
120
+ var msg = plugin.name;
121
+
122
+ if( typeof that.plugins.install[ $slug] !== "undefined" ) {
123
+ msg = demo_contents_params.messages.plugin_not_installed.format( plugin.name );
124
+ } else {
125
+ if( typeof that.plugins.activate[ $slug] !== "undefined" ) {
126
+ msg = demo_contents_params.messages.plugin_not_activated.format( plugin.name );
127
  }
128
+ }
 
129
 
130
+ var $item = $('<div data-slug="' + $slug + '" class="demo-contents-child-item dc-unknown-status demo-contents-plugin-' + $slug + '">'+msg+'</div>');
131
+ $child_steps.append($item);
132
+ $item.attr('data-plugin', $slug);
133
+ });
134
+ } else {
135
+ $list_install_plugins.hide();
136
  }
137
+
138
+
139
+ if ( demo_contents_viewing_theme.activate ) {
140
+ $( '.demo-contents--activate-notice' ).hide();
141
+ } else {
142
+ $( '.demo-contents-import-progress' ).hide();
143
+ $( '.demo-contents--activate-notice' ).show();
144
+
145
+ var activate_theme_btn = $( '<a href="#" class="demo-contents--activate-now button button-primary">'+demo_contents_params.activate_theme+'</a>' );
146
+ $( '.demo-contents--import-now' ).replaceWith( activate_theme_btn );
147
+ }
148
+
149
+ },
150
+ installPlugins: function() {
151
+ var that = this;
152
+ that.plugins = demo_contents_working_plugins;
153
+ // Install Plugins
154
+ var $list_install_plugins = $( '.demo-contents-install-plugins' );
155
+ that.loading_step( $list_install_plugins );
156
+ console.log( 'Being installing plugins....' );
157
+ var $child_steps = $list_install_plugins.find( '.demo-contents--child-steps' );
158
+ var n = _.size( that.plugins.install );
159
+ if ( n > 0 ) {
160
+
161
+ var callback = function( current ){
162
+ if ( current.length ) {
163
+ var slug = current.attr( 'data-plugin' );
164
+ if ( typeof that.plugins.install[ slug ] === "undefined" ) {
165
+ var next = current.next();
166
+ callback( next );
167
+ } else {
168
+ var plugin = that.plugins.install[ slug ];
169
+ var msg = demo_contents_params.messages.plugin_installing.format( plugin.name );
170
+ console.log( msg );
171
+ current.html( msg );
172
+
173
+ $.post( plugin.page_url, plugin.args, function (res) {
174
+ //console.log(plugin.name + ' Install Completed');
175
+ plugin.args.action = demo_contents_params.action_active_plugin;
176
+ that.plugins.activate[ slug ] = plugin;
177
+ var msg = demo_contents_params.messages.plugin_installed.format( plugin.name );
178
+ console.log( msg );
179
+ current.html( msg );
180
+ var next = current.next();
181
+ callback( next );
182
+ }).fail(function() {
183
+ demo_contents_working_plugins = that.plugins;
184
+ console.log( 'Plugins install failed' );
185
+ $document.trigger( 'demo_contents_plugins_install_completed' );
186
+ });
187
+ }
188
+ } else {
189
+ demo_contents_working_plugins = that.plugins;
190
+ console.log( 'Plugins install completed' );
191
+ $document.trigger( 'demo_contents_plugins_install_completed' );
192
+ }
193
+ };
194
+
195
+ var current = $child_steps.find( '.demo-contents-child-item' ).eq( 0 );
196
+ callback( current );
197
+ } else {
198
+ demo_contents_working_plugins = that.plugins;
199
+ console.log( 'Plugins install completed - 0' );
200
+ //$list_install_plugins.hide();
201
+ $document.trigger( 'demo_contents_plugins_install_completed' );
202
+ }
203
+
204
+ // that.completed_step( $list_install_plugins, 'demo_contents_plugins_install_completed' );
205
+
206
+ },
207
+ activePlugins: function(){
208
+ var that = this;
209
+ that.plugins = demo_contents_working_plugins;
210
+
211
+ that.plugins.activate = $.extend({},that.plugins.activate );
212
+ console.log( 'activePlugins', that.plugins );
213
+ var $list_active_plugins = $( '.demo-contents-install-plugins' );
214
+ that.loading_step( $list_active_plugins );
215
+ var $child_steps = $list_active_plugins.find( '.demo-contents--child-steps' );
216
+ var n = _.size( that.plugins.activate );
217
+ console.log( 'Being activate plugins....' );
218
+ if ( n > 0 ) {
219
+ var callback = function (current) {
220
+ if (current.length) {
221
+ var slug = current.attr('data-plugin');
222
+
223
+ if ( typeof that.plugins.activate[ slug ] === "undefined" ) {
224
+ var next = current.next();
225
+ callback( next );
226
+ } else {
227
+ var plugin = that.plugins.activate[slug];
228
+ var msg = demo_contents_params.messages.plugin_activating.format( plugin.name );
229
+ console.log( msg );
230
+ current.html( msg );
231
+ $.post(plugin.page_url, plugin.args, function (res) {
232
+
233
+ var msg = demo_contents_params.messages.plugin_activated.format( plugin.name );
234
+ console.log( msg );
235
+ current.html( msg );
236
+ var next = current.next();
237
+ callback(next);
238
+ }).fail(function() {
239
+ console.log( 'Plugins activate failed' );
240
+ that.completed_step( $list_active_plugins, 'demo_contents_plugins_active_completed' );
241
+ });
242
+ }
243
+
244
+ } else {
245
+ console.log(' Activated all plugins');
246
+ that.completed_step( $list_active_plugins, 'demo_contents_plugins_active_completed' );
247
+ }
248
+ };
249
+
250
+ var current = $child_steps.find( '.demo-contents-child-item' ).eq( 0 );
251
+ callback( current );
252
+
253
+ } else {
254
+ // $list_active_plugins.hide();
255
+ console.log(' Activated all plugins - 0');
256
+ $list_active_plugins.removeClass('demo-contents--running demo-contents--waiting').addClass('demo-contents--completed');
257
+ $document.trigger('demo_contents_plugins_active_completed');
258
+ }
259
+
260
+ },
261
+ ajax: function( doing, complete_cb, fail_cb ){
262
+ console.log( 'Being....', doing );
263
+ $.ajax( {
264
+ url: demo_contents_params.ajaxurl,
265
+ data: {
266
+ action: 'demo_contents__import',
267
+ doing: doing,
268
+ current_theme: demo_contents_viewing_theme,
269
+ theme: '', // Import demo for theme ?
270
+ version: '' // Current demo version ?
271
+ },
272
+ type: 'GET',
273
+ dataType: 'json',
274
+ success: function( res ){
275
+ console.log( res );
276
+ if ( typeof complete_cb === 'function' ) {
277
+ complete_cb( res );
278
+ }
279
+ console.log( 'Completed: '+ doing, res );
280
+ $document.trigger( 'demo_contents_'+doing+'_completed' );
281
+ },
282
+ fail: function( res ){
283
+ if ( typeof fail_cb === 'function' ) {
284
+ fail_cb( res );
285
+ }
286
+ console.log( 'Failed: '+ doing, res );
287
+ $document.trigger( 'demo_contents_'+doing+'_failed' );
288
+ $document.trigger( 'demo_contents_ajax_failed', [ doing ] );
289
+ }
290
+
291
+ } )
292
+ },
293
+ import_users: function(){
294
+ var step = $( '.demo-contents-import-users' );
295
+ var that = this;
296
+ that.loading_step( step );
297
+ this.ajax( 'import_users', function(){
298
+ that.completed_step( step );
299
+ } );
300
+ },
301
+ import_categories: function(){
302
+ var step = $( '.demo-contents-import-categories' );
303
+ var that = this;
304
+ that.loading_step( step );
305
+ this.ajax( 'import_categories', function(){
306
+ that.completed_step( step );
307
+ } );
308
+ },
309
+ import_tags: function(){
310
+ var step = $( '.demo-contents-import-tags' );
311
+ var that = this;
312
+ that.loading_step( step );
313
+ this.ajax( 'import_tags', function(){
314
+ that.completed_step( step );
315
+ } );
316
+ },
317
+ import_taxs: function(){
318
+ var step = $( '.demo-contents-import-taxs' );
319
+ var that = this;
320
+ that.loading_step( step );
321
+ this.ajax( 'import_taxs', function(){
322
+ that.completed_step( step );
323
+ } );
324
+ },
325
+ import_posts: function(){
326
+ var step = $( '.demo-contents-import-posts' );
327
+ var that = this;
328
+ that.loading_step( step );
329
+ this.ajax( 'import_posts', function(){
330
+ that.completed_step( step );
331
+ } );
332
+ },
333
+
334
+ import_theme_options: function(){
335
+ var step = $( '.demo-contents-import-theme-options' );
336
+ var that = this;
337
+ that.loading_step( step );
338
+ this.ajax( 'import_theme_options', function(){
339
+ that.completed_step( step );
340
+ } );
341
+ },
342
+
343
+ import_widgets: function(){
344
+ var step = $( '.demo-contents-import-widgets' );
345
+ var that = this;
346
+ that.loading_step( step );
347
+ this.ajax( 'import_widgets', function(){
348
+ that.completed_step( step );
349
+ } );
350
+ },
351
+
352
+ import_customize: function(){
353
+ var step = $( '.demo-contents-import-customize' );
354
+ var that = this;
355
+ that.loading_step( step );
356
+ this.ajax( 'import_customize', function (){
357
+ that.completed_step( step );
358
+ } );
359
+ },
360
+
361
+ toggle_collapse: function(){
362
+ $document .on( 'click', '.demo-contents-collapse-sidebar', function( e ){
363
+ $( '#demo-contents--preview' ).toggleClass('ft-preview-collapse');
364
+ } );
365
+ },
366
+
367
+ done: function(){
368
+ console.log( 'All done' );
369
+ this.end_loading();
370
+ $( '.demo-contents--import-now' ).replaceWith( '<a href="'+demo_contents_params.home+'" class="button button-primary">'+demo_contents_params.btn_done_label+'</a>' );
371
+ },
372
+
373
+ failed: function(){
374
+ console.log( 'Import failed' );
375
+ $( '.demo-contents--import-now' ).replaceWith( '<span class="button button-secondary">'+demo_contents_params.failed_msg+'</span>' );
376
+ },
377
+
378
+ preview: function(){
379
+ var that = this;
380
+ $document .on( 'click', '.demo-contents-themes-listing .theme', function( e ){
381
+ e.preventDefault();
382
+ console.log( 'ok' );
383
+ var t = $( this );
384
+ var btn = $( '.demo-contents--preview-theme-btn', t );
385
+ var theme = btn.closest('.theme');
386
+ var slug = btn.attr( 'data-theme-slug' ) || '';
387
+ var name = btn.attr( 'data-name' ) || '';
388
+ var demo_version = btn.attr( 'data-demo-version' ) || '';
389
+ var demo_name = btn.attr( 'data-demo-version-name' ) || '';
390
+ var demo_url = btn.attr( 'data-demo-url' ) || '';
391
+ var img = $( '.theme-screenshot img', theme ).attr( 'src' );
392
+ if ( demo_url.indexOf( 'http' ) !== 0 ) {
393
+ demo_url = 'https://demos.famethemes.com/'+slug+'/';
394
+ }
395
+ $( '#demo-contents--preview' ).remove();
396
+
397
+ demo_contents_viewing_theme = {
398
+ name: name,
399
+ slug: slug,
400
+ demo_version: demo_version,
401
+ demo_name: demo_name,
402
+ demoURL: demo_url,
403
+ img: img,
404
+ activate: false
405
+ };
406
+
407
+ if ( typeof demo_contents_params.installed_themes[ slug ] !== "undefined" ) {
408
+ if ( demo_contents_params.installed_themes[ slug ].activate ) {
409
+ demo_contents_viewing_theme.activate = true;
410
+ }
411
+ }
412
+
413
+ var previewHtml = template( demo_contents_viewing_theme );
414
+ $( 'body' ).append( previewHtml );
415
+ $( 'body' ).addClass( 'demo-contents-body-viewing' );
416
+
417
+ that.preparing_plugins();
418
+
419
+ $document.trigger( 'demo_contents_preview_opened' );
420
+
421
+ } );
422
+
423
+ $document.on( 'click', '.demo-contents-close', function( e ) {
424
+ e.preventDefault();
425
+ if ( demo_contents_import_running ) {
426
+ var c = confirm( demo_contents_params.confirm_leave ) ;
427
+ if ( c ) {
428
+ demo_contents_import_running = false;
429
+ $( this ).closest('#demo-contents--preview').remove();
430
+ $( 'body' ).removeClass( 'demo-contents-body-viewing' );
431
+ }
432
+ } else {
433
+ $( this ).closest('#demo-contents--preview').remove();
434
+ $( 'body' ).removeClass( 'demo-contents-body-viewing' );
435
+ }
436
+
437
+ } );
438
+
439
+ },
440
+
441
+ checking_resources: function(){
442
+ var that = this;
443
+ var button = $( '.demo-contents--import-now, .demo-contents--activate-now' );
444
+ button.html( demo_contents_params.checking_resource );
445
+ button.addClass( 'updating-message' );
446
+ button.addClass( 'disabled' );
447
+ that.ajax( 'checking_resources', function( res ){
448
+ if ( res.success ) {
449
+ button.removeClass( 'disabled' );
450
+ button.removeClass( 'updating-message' );
451
+ if ( demo_contents_viewing_theme.activate ) {
452
+ button.html( demo_contents_params.import_now );
453
+ } else {
454
+ button.html( demo_contents_params.activate_theme );
455
+ }
456
+ } else {
457
+ $( '.demo-contents--activate-notice.resources-not-found' ).show().removeClass( 'demo-contents-hide' );
458
+ $( '.demo-contents--activate-notice.resources-not-found .demo-contents--msg' ).addClass('not-found-data').show().html( res.data );
459
+ $( '.demo-contents-import-progress' ).hide();
460
+ var text = demo_contents_viewing_theme.activate ? demo_contents_params.import_now : demo_contents_params.activate_theme;
461
+ button.replaceWith( '<a href="#" class="demo-contents--no-data-btn button button-secondary disabled disable">'+text+'</a>' );
462
+ }
463
+ } );
464
+ },
465
+
466
+ init: function(){
467
+ var that = this;
468
+
469
+ that.preview();
470
+ that.toggle_collapse();
471
+
472
+ $document.on( 'demo_contents_ready', function(){
473
+ $( '.demo-contents--activate-notice.resources-not-found ').slideUp(200).addClass( 'content-demos-hide' );
474
+ that.loading();
475
+ that.installPlugins();
476
+ } );
477
+
478
+ $document.on( 'demo_contents_plugins_install_completed', function(){
479
+ that.activePlugins();
480
+ } );
481
+
482
+ $document.on( 'demo_contents_plugins_active_completed', function(){
483
+ that.import_users();
484
+ } );
485
+
486
+ $document.on( 'demo_contents_import_users_completed', function(){
487
+ that.import_categories();
488
+ } );
489
+
490
+ $document.on( 'demo_contents_import_categories_completed', function(){
491
+ that.import_tags();
492
+ } );
493
+
494
+ $document.on( 'demo_contents_import_tags_completed', function(){
495
+ that.import_taxs();
496
+ } );
497
+
498
+ $document.on( 'demo_contents_import_taxs_completed', function(){
499
+ that.import_posts();
500
+ } );
501
+
502
+ $document.on( 'demo_contents_import_posts_completed', function(){
503
+ that.import_theme_options();
504
+ } );
505
+
506
+ $document.on( 'demo_contents_import_theme_options_completed', function(){
507
+ that.import_widgets();
508
+ } );
509
+
510
+ $document.on( 'demo_contents_import_widgets_completed', function(){
511
+ that.import_customize();
512
+ } );
513
+
514
+ $document.on( 'demo_contents_import_customize_completed', function(){
515
+ that.done();
516
+ } );
517
+
518
+ $document.on( 'demo_contents_ajax_failed', function(){
519
+ that.failed();
520
+ } );
521
+
522
+
523
+ // Toggle Heading
524
+ $document.on( 'click', '.demo-contents--step', function( e ){
525
+ e.preventDefault();
526
+ $( '.demo-contents--child-steps', $( this ) ).toggleClass( 'demo-contents--show' );
527
+ } );
528
+
529
+ // Import now click
530
+ $document.on( 'click', '.demo-contents--import-now', function( e ) {
531
+ e.preventDefault();
532
+ if ( ! $( this ).hasClass( 'updating-message' ) ) {
533
+ $( this ).addClass( 'updating-message' );
534
+ $( this ).html( demo_contents_params.importing );
535
+ $document.trigger( 'demo_contents_ready' );
536
+ }
537
+ } );
538
+
539
+
540
+ // Activate Theme Click
541
+ $document.on( 'click', '.demo-contents--activate-now', function( e ) {
542
+ e.preventDefault();
543
+ var btn = $( this );
544
+ if ( ! btn.hasClass( 'updating-message' ) ) {
545
+ btn.addClass( 'updating-message' );
546
+ that.ajax( 'activate_theme', function( res ){
547
+ var new_btn = $( '<a href="#" class="demo-contents--checking-resource updating-message button button-primary">' + demo_contents_params.checking_theme + '</a>' );
548
+ btn.replaceWith( new_btn );
549
+
550
+ demo_contents_params.current_theme = demo_contents_viewing_theme.slug;
551
+ demo_contents_params.current_child_theme = demo_contents_viewing_theme.slug;
552
+
553
+ $.get( demo_contents_params.theme_url, { __checking_plugins: 1 }, function( res ){
554
+ console.log( 'Checking plugin completed: ', demo_contents_params.import_now );
555
+ $( '.demo-contents--checking-resource, .demo-contents--activate-now' ).replaceWith('<a href="#" class="demo-contents--import-now button button-primary">' + demo_contents_params.import_now + '</a>');
556
+ if ( res.success ) {
557
+ demo_contents_viewing_theme.activate = true;
558
+ that.preparing_plugins( res.data );
559
+ $( '.demo-contents--activate-notice' ).slideUp( 200 );
560
+ $( '.demo-contents-import-progress' ).slideDown(200);
561
+ }
562
+ } );
563
+
564
+ } );
565
+ }
566
+ } );
567
+
568
+ $document.on( 'demo_contents_preview_opened', function(){
569
+ // that.loading();
570
+ that.checking_resources();
571
+ //$document.trigger( 'demo_contents_import_theme_options_completed' );
572
+ //$document.trigger( 'demo_contents_import_widgets_completed' );
573
+ } );
574
+
575
+
576
+ // Custom upload demo file
577
+ var Media = wp.media({
578
+ title: wp.media.view.l10n.addMedia,
579
+ multiple: false,
580
+ // library:
581
+ });
582
+
583
+ that.uploading_file = false;
584
+
585
+ $document.on( 'click', '.demo-contents--upload-xml', function(e){
586
+ e.preventDefault();
587
+ Media.open();
588
+ that.uploading_file = 'xml';
589
+ } );
590
+
591
+ $document.on( 'click', '.demo-contents--upload-json', function(e){
592
+ e.preventDefault();
593
+ Media.open();
594
+ that.uploading_file = 'json';
595
+ } );
596
+
597
+ var check_upload = function(){
598
+ if ( typeof demo_contents_viewing_theme.xml_id !== "undefined"
599
+ &&typeof demo_contents_viewing_theme.json_id !== "undefined"
600
+ && demo_contents_viewing_theme.xml_id
601
+ && demo_contents_viewing_theme.json_id
602
+ ) {
603
+ if ( demo_contents_viewing_theme.activate ) {
604
+ $( '.demo-contents-import-progress' ).show();
605
+ $( '.demo-contents--no-data-btn' ).replaceWith( '<a href="#" class="demo-contents--import-now button button-primary">' + demo_contents_params.import_now + '</a>' );
606
+ } else {
607
+ $( '.demo-contents--no-data-btn' ).replaceWith( '<a href="#" class="demo-contents--activate-now button button-primary">'+demo_contents_params.activate_theme+'</a>' );
608
+ }
609
+ }
610
+ };
611
+
612
+ Media.on('select', function () {
613
+ var attachment = Media.state().get('selection').first().toJSON();
614
+ var id = attachment.id;
615
+ var file_name = attachment.filename;
616
+ var ext = file_name.split('.').pop();
617
+ if (that.uploading_file == 'xml') {
618
+ if (ext.toLowerCase() == 'xml') {
619
+ demo_contents_viewing_theme.xml_id = id;
620
+ $('.demo-contents--upload-xml').html(file_name);
621
+ check_upload();
622
+ }
623
+ }
624
+
625
+ if (that.uploading_file == 'json') {
626
+ if (ext.toLowerCase() == 'txt' || ext.toLowerCase() == 'json') {
627
+ demo_contents_viewing_theme.json_id = id;
628
+ $('.demo-contents--upload-json').html(file_name);
629
+ check_upload();
630
+ }
631
+ }
632
+
633
+ });
634
+
635
+ // END Custom upload demo file
636
+
637
+ }
638
+ };
639
+
640
+ $.fn.ftDemoContent = function() {
641
+ ftDemoContents.init();
642
+ };
643
+
644
+
645
+ }( jQuery ));
646
+
647
+ jQuery( document ).ready( function( $ ){
648
+ $( document ).ftDemoContent();
649
+ });
650
 
651
 
652
 
 
 
assets/scss/style.scss ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .demo-contents-import-box {
3
+ background: #fff none repeat scroll 0 0;
4
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
5
+ font-size: 14px;
6
+ line-height: 2.1em;
7
+ margin-bottom: 25px;
8
+ overflow: auto;
9
+ padding: 15px;
10
+ position: relative;
11
+ max-width: 1050px;
12
+ }
13
+ .demo-contents-import-box.demo-contents-welcome {
14
+ padding: 0px 20px 10px 25px;
15
+ }
16
+ .demo-contents-import-box h3 {
17
+ font-size: 22px;
18
+ font-weight: 500;
19
+ margin: 0px 0px 15px;
20
+ }
21
+ .demo-contents-import-box li {
22
+ margin: 0px;
23
+ padding-left: 15px;
24
+ list-style: inside;
25
+ }
26
+
27
+ .demo-contents--current-theme {
28
+ position: relative;
29
+ .more-details {
30
+ opacity: 1 !important;
31
+ z-index: 3;
32
+ }
33
+ }
34
+ .demo-contents-hide {
35
+ display: none !important;
36
+ }
37
+ .demo-contents .more-details {
38
+ text-decoration: none !important;
39
+ }
40
+
41
+ .demo-contents .theme-overlay .theme-wrap {
42
+ box-shadow: 0 1px 1px -1px rgba(0,0,0,.1);
43
+ border: 1px solid #ddd;
44
+ }
45
+ .demo-contents .theme-overlay .theme-wrap {
46
+ position: relative;
47
+ top: auto; bottom: auto; left: auto; right: auto;
48
+ padding-top: 50%;
49
+ margin-bottom: 40px;
50
+ }
51
+ .demo-contents .theme-overlay .theme-about {
52
+ top: 25px;
53
+ }
54
+ .demo-contents .theme-overlay .theme-overlay .theme-wrap {
55
+ z-index: 1;
56
+ }
57
+ .demo-contents .theme .theme-actions, .demo-import-tab-content .theme .theme-actions {
58
+ opacity: 1;
59
+ }
60
+ .demo-contents-import-progress {
61
+ background: #ffffff;
62
+ padding: 15px 15px 30px;
63
+ display: block;
64
+ }
65
+
66
+ .demo-contents-child-item {
67
+ margin: 5px 0px;
68
+ }
69
+
70
+ .demo-contents--activate-notice {
71
+ padding: 15px;
72
+ margin: 30px 0px;
73
+ &.resources-not-found {
74
+ margin-top: 0px;
75
+ }
76
+ }
77
+
78
+ @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
79
+ @-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
80
+ @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
81
+
82
+ .demo-contents--theme-thumbnail {
83
+ img {
84
+ max-width: 100%;
85
+ height: auto;
86
+ }
87
+ padding: 10px;
88
+ background: #ddd;
89
+ }
90
+
91
+ .demo-contents--step {
92
+ position: relative;
93
+ cursor: pointer;
94
+ border-bottom: 1px solid #ddd;
95
+ padding-bottom: 10px;
96
+ padding-top: 10px;
97
+ &:last-child {
98
+ border-bottom: 0px none;
99
+ }
100
+ .demo-contents--step-heading {
101
+ font-size: 12px;
102
+ font-weight: bold;
103
+ line-height: 18px;
104
+ }
105
+ .demo-contents--child-steps {
106
+ display: none;
107
+ &.demo-contents--show {
108
+ display: block;
109
+ }
110
+ }
111
+ .demo-contents--status {
112
+ background: none;
113
+ border: none !important;
114
+ -webkit-box-shadow: none;
115
+ box-shadow: none;
116
+ padding: 0px;
117
+ position: absolute;
118
+ top: 7px;
119
+ right: 0px;
120
+ line-height: 24px;
121
+ width: 24px;
122
+ height: 24px;
123
+ display: block;
124
+
125
+ &:before {
126
+ font-family: "dashicons";
127
+ font: 400 24px/1 dashicons;
128
+ -webkit-font-smoothing: antialiased;
129
+ -moz-osx-font-smoothing: grayscale;
130
+ width: 24px;
131
+ height: 24px;
132
+ display: block;
133
+ font-weight: normal;
134
+ }
135
+ }
136
+
137
+ &.demo-contents--running {
138
+ .demo-contents--status {
139
+ -webkit-animation:spin 2s linear infinite;
140
+ -moz-animation:spin 2s linear infinite;
141
+ animation:spin 2s linear infinite;
142
+ color: #f56e28;
143
+ &:before {
144
+ content: "\f463";
145
+ }
146
+ }
147
+ }
148
+
149
+ &.demo-contents--waiting {
150
+ .demo-contents--status {
151
+ &:before {
152
+ content: "\f469";
153
+ }
154
+ }
155
+ }
156
+
157
+ &.demo-contents--completed {
158
+ .demo-contents--status {
159
+ color: #65ae1f;
160
+ &::before {
161
+ content: "\f147";
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+
168
+ body.demo-contents-body-viewing {
169
+ overflow: hidden !important;
170
+ }
171
+
172
+
173
+ #demo-contents--preview {
174
+ z-index: 99999;
175
+ background: #eee;
176
+ position: fixed;
177
+ top: 0px;
178
+ left: 0px;
179
+ bottom: 0px;
180
+ display: block;
181
+ width: 100%;
182
+
183
+ .demo-contents-close {
184
+ display: block;
185
+ float: left;
186
+ width: 45px;
187
+ height: 45px;
188
+ padding-right: 2px;
189
+ background: #eee;
190
+ border-right: 1px solid #ddd;
191
+ color: #444;
192
+ cursor: pointer;
193
+ text-decoration: none;
194
+ position: absolute;
195
+ top: 0px;
196
+ left: 0px;
197
+ -webkit-transition: color .1s ease-in-out,background .1s ease-in-out;
198
+ transition: color .1s ease-in-out,background .1s ease-in-out;
199
+ z-index: 99999;
200
+ &:before {
201
+ font: 400 22px/1 dashicons;
202
+ content: "\f335";
203
+ position: relative;
204
+ top: 12px;
205
+ left: 13px;
206
+ }
207
+ }
208
+ .demo-contents-collapse-sidebar {
209
+ z-index: 99999;
210
+ bottom: 5px;
211
+ left: 0;
212
+ padding: 9px 0 9px 10px;
213
+ height: 45px;
214
+ color: #656a6f;
215
+ outline: 0;
216
+ line-height: 1;
217
+ background-color: transparent!important;
218
+ border: none!important;
219
+ -webkit-box-shadow: none!important;
220
+ box-shadow: none!important;
221
+ -webkit-border-radius: 0!important;
222
+ border-radius: 0!important;
223
+ position: fixed;
224
+
225
+ .collapse-sidebar-arrow:before {
226
+ display: block;
227
+ float: left;
228
+ content: "\f148";
229
+ font: 400 20px/1 dashicons;
230
+ speak: none;
231
+ padding: 0;
232
+ -webkit-font-smoothing: antialiased;
233
+ -moz-osx-font-smoothing: grayscale;
234
+ border: 1px solid transparent;
235
+
236
+ -webkit-border-radius: 50%;
237
+ -moz-border-radius: 50%;
238
+ border-radius: 50%;
239
+ }
240
+ .collapse-sidebar-label {
241
+ display: inline-block;
242
+ vertical-align: middle;
243
+ line-height: 20px;
244
+ margin-left: 5px;
245
+ -webkit-transition: all .3s; /* For Safari 3.1 to 6.0 */
246
+ transition: all .3s;
247
+ }
248
+
249
+ &:hover {
250
+ color: #0073aa;
251
+ .collapse-sidebar-arrow:before {
252
+ border-color: #0073aa;
253
+ }
254
+ }
255
+ }
256
+
257
+ #demo-contents-sidebar-topbar {
258
+ display: block;
259
+ text-align: right;
260
+ border-bottom: 1px solid #ddd;
261
+ padding: 10px 10px 10px 55px;
262
+ background: #eee;
263
+ height: 25px;
264
+ }
265
+ .ft-theme-name {
266
+ font-size: 20px;
267
+ font-weight: bold;
268
+ line-height: 24px;
269
+ }
270
+ #demo-contents-sidebar {
271
+ width: 300px;
272
+ top: 0px;
273
+ left: 0px;
274
+ bottom: 0px;
275
+ display: block;
276
+ border-right: 1px solid #ddd;
277
+ position: fixed;
278
+ background: #fff;
279
+ -webkit-transition: all .3s; /* For Safari 3.1 to 6.0 */
280
+ transition: all .3s;
281
+ overflow: hidden;
282
+ }
283
+ &.ft-preview-collapse {
284
+ #demo-contents-sidebar {
285
+ left: -301px;
286
+
287
+ }
288
+ #demo-contents-viewing {
289
+ margin-left: 0px;
290
+ }
291
+ .collapse-sidebar-label {
292
+ opacity: 0 !important;
293
+ }
294
+ }
295
+
296
+ #demo-contents-sidebar-heading {
297
+ background: #fff;
298
+ color: #555d66;
299
+ margin: 0;
300
+ padding: 12px 15px 15px;
301
+ position: relative;
302
+ font-size: 20px;
303
+ font-weight: 200;
304
+ line-height: 26px;
305
+ display: block;
306
+ overflow: hidden;
307
+ white-space: nowrap;
308
+ border-bottom: 1px solid #ddd;
309
+ span {
310
+ display: block;
311
+ font-size: 12px;
312
+ }
313
+ }
314
+
315
+ #demo-contents-viewing {
316
+ margin-left: 301px;
317
+ position: relative;
318
+ height: 100%;
319
+ display: block;
320
+ iframe {
321
+ width: 100%;
322
+ height: 100%;
323
+ display: block;
324
+ }
325
+
326
+ -webkit-transition: all .3s; /* For Safari 3.1 to 6.0 */
327
+ transition: all .3s;
328
+ overflow: hidden;
329
+ }
330
+ #demo-contents-sidebar-content {
331
+ position: absolute;
332
+ top: 46px;
333
+ bottom: 49px;
334
+ display: block;
335
+ left: 0px;
336
+ width: 100%;
337
+ overflow: auto;
338
+ }
339
+ #demo-contents-sidebar-footer {
340
+ position: absolute;
341
+ bottom: 0px;
342
+ right: 0px;
343
+ display: block;
344
+ left: 0px;
345
+ padding: 20px 20px;
346
+ text-align: right;
347
+ background: #eee;
348
+ border-top: 1px solid #ddd;
349
+ }
350
+
351
+ // Upload form
352
+ .demo-contents---upload {
353
+ button {
354
+ display: block;
355
+ width: 100%;
356
+ }
357
+ text-align: center;
358
+ padding: 15px 20px;
359
+ background: #eee;
360
+ }
361
+
362
+ }
famethemes-demo-importer.php CHANGED
@@ -5,329 +5,50 @@ Plugin URI: https://github.com/FameThemes/famethemes-demo-importer
5
  Description: Demo data import tool for FameThemes's themes.
6
  Author: famethemes
7
  Author URI: http://www.famethemes.com/
8
- Version: 1.0.3
9
- Text Domain: ftdi
10
  License: GPL version 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
  */
12
 
13
 
14
- class FT_Demo_Content_Importer {
15
- public $dir;
16
- public $url;
17
- public $git_repo = 'https://raw.githubusercontent.com/FameThemes/famethemes-xml-demos/master/';
18
- function __construct( ){
19
-
20
- $this->url = trailingslashit( plugins_url('', __FILE__) );
21
- $this->dir = trailingslashit( plugin_dir_path( __FILE__) );
22
-
23
- require_once $this->dir.'inc/class-demo-content.php';
24
- add_action( 'wp_ajax_ft_demo_import_content', array( $this, 'ajax_import' ) );
25
- add_action( 'wp_ajax_ft_demo_import_download', array( $this, 'ajax_download' ) );
26
- add_action( 'wp_ajax_ft_demo_export', array( $this, 'ajax_export' ) );
27
-
28
- $template_slug = get_option( 'template' );
29
- $theme_slug = get_option( 'stylesheet' );
30
- // child theme active
31
- if ( $template_slug != $theme_slug ) {
32
- add_action( $template_slug.'_demo_import_content_tab', array( $this, 'display_import' ) );
33
- } else {
34
- add_action( $theme_slug.'_demo_import_content_tab', array( $this, 'display_import' ) );
35
- }
36
-
37
- add_action( 'customize_controls_print_footer_scripts', array( $this, 'update_customizer_keys' ) );
38
- if ( is_admin() ) {
39
- add_action( 'admin_enqueue_scripts', array( $this, 'css' ) );
40
- }
41
-
42
- }
43
 
44
- function update_customizer_keys(){
45
- $key = 'ft_demo_customizer_keys';
46
- $theme_slug = get_option( 'stylesheet' );
47
- $data = get_option( $key );
48
- if ( ! is_array( $data ) ) {
49
- $data = array();
50
- }
51
-
52
- global $wp_customize;
53
-
54
- $pages = get_pages();
55
- $option_pages = array();
56
- $option_pages[0] = esc_html__( 'Select page', 'ftdi' );
57
- foreach( $pages as $p ){
58
- $option_pages[ $p->ID ] = $p->post_title;
59
- }
60
-
61
- foreach ( $wp_customize->controls() as $k => $c ) {
62
- if ( $c->type == 'repeatable' ) {
63
- $keys = array();
64
- if ( property_exists( $c, 'fields' ) ){
65
- foreach ( $c->fields as $field_id => $field ) {
66
- if( isset( $field['options'] ) && is_array( $field['options'] ) ) {
67
- $result = array_diff( $option_pages, $field['options'] );
68
- if ( empty( $result ) ) { // if this option use page
69
- $info = array(
70
- 'key' => $field_id,
71
- 'type' => 'page',
72
- );
73
- array_push( $keys, $info );
74
- }
75
- } elseif( isset( $field['data_type'] ) && in_array( $field['data_type'] , array( 'post', 'page' ) ) ) {
76
- $info = array(
77
- 'key' => $field_id,
78
- 'type' => 'page',
79
- );
80
- array_push( $keys, $info );
81
- } elseif( $field['type'] == 'media' ) {
82
- $info = array(
83
- 'key' => $field_id,
84
- 'type' => 'media',
85
- );
86
- array_push( $keys, $info );
87
- }
88
- }
89
- }
90
-
91
- $data[ $theme_slug ][ $k ] = $keys;
92
-
93
- } else if ( $c->type == 'media' ) { // wp media
94
- $data[ $theme_slug ][ $k ] = $c->type;
95
- }
96
-
97
- }
98
- update_option( $key, $data );
99
- }
100
-
101
- function get_recommend_plugins(){
102
- $recommend_plugins = get_theme_support( 'recommend-plugins' );
103
- if ( is_array( $recommend_plugins ) && isset( $recommend_plugins[0] ) ){
104
- $recommend_plugins = $recommend_plugins[0];
105
- } else {
106
- $recommend_plugins[] = array();
107
- }
108
-
109
- $recommend_plugins = array_filter( $recommend_plugins );
110
-
111
- return $recommend_plugins;
112
- }
113
-
114
- function has_inactive_recommend_plugins( $recommend_plugins = false ){
115
- if ( ! $recommend_plugins ) {
116
- $recommend_plugins = $this->get_recommend_plugins( ) ;
117
- }
118
- $all_active = true;
119
- if ( ! empty( $recommend_plugins ) ) {
120
- foreach ( $recommend_plugins as $plugin_slug => $plugin_info ) {
121
- $plugin_info = wp_parse_args( $plugin_info, array(
122
- 'name' => '',
123
- 'active_filename' => '',
124
- ) );
125
- if ( $plugin_info['active_filename'] ) {
126
- $active_file_name = $plugin_info['active_filename'] ;
127
- } else {
128
- $active_file_name = $plugin_slug . '/' . $plugin_slug . '.php';
129
- }
130
- if ( ! is_plugin_active( $active_file_name ) ) {
131
- $all_active = false;
132
- }
133
- }
134
- }
135
-
136
- return ( $all_active ) ? false : true;
137
- }
138
 
139
- function render_recommend_plugins( $recommend_plugins = false ){
140
- if ( ! $recommend_plugins ) {
141
- $recommend_plugins = $this->get_recommend_plugins( ) ;
142
- }
143
-
144
- if ( empty( $recommend_plugins ) ) {
145
- return false;
 
 
 
 
146
  }
147
- foreach ( $recommend_plugins as $plugin_slug => $plugin_info ) {
148
- $plugin_info = wp_parse_args( $plugin_info, array(
149
- 'name' => '',
150
- 'active_filename' => '',
151
- ) );
152
- $plugin_name = $plugin_info['name'];
153
- $status = is_dir( WP_PLUGIN_DIR . '/' . $plugin_slug );
154
- $button_class = 'install-now button';
155
- if ( $plugin_info['active_filename'] ) {
156
- $active_file_name = $plugin_info['active_filename'] ;
157
- } else {
158
- $active_file_name = $plugin_slug . '/' . $plugin_slug . '.php';
159
- }
160
-
161
- if ( ! is_plugin_active( $active_file_name ) ) {
162
- $button_txt = esc_html__( 'Install Now', 'ftdi' );
163
- if ( ! $status ) {
164
- $install_url = wp_nonce_url(
165
- add_query_arg(
166
- array(
167
- 'action' => 'install-plugin',
168
- 'plugin' => $plugin_slug
169
- ),
170
- network_admin_url( 'update.php' )
171
- ),
172
- 'install-plugin_'.$plugin_slug
173
- );
174
-
175
- } else {
176
- $install_url = add_query_arg(array(
177
- 'action' => 'activate',
178
- 'plugin' => rawurlencode( $active_file_name ),
179
- 'plugin_status' => 'all',
180
- 'paged' => '1',
181
- '_wpnonce' => wp_create_nonce('activate-plugin_' . $active_file_name ),
182
- ), network_admin_url('plugins.php'));
183
- $button_class = 'activate-now button-primary';
184
- $button_txt = esc_html__( 'Active Now', 'ftdi' );
185
- }
186
-
187
- $detail_link = add_query_arg(
188
- array(
189
- 'tab' => 'plugin-information',
190
- 'plugin' => $plugin_slug,
191
- 'TB_iframe' => 'true',
192
- 'width' => '772',
193
- 'height' => '349',
194
-
195
- ),
196
- network_admin_url( 'plugin-install.php' )
197
- );
198
-
199
- echo '<div class="rcp">';
200
- echo '<h4 class="rcp-name">';
201
- echo esc_html( $plugin_name );
202
- echo '</h4>';
203
- echo '<p class="action-btn plugin-card-'.esc_attr( $plugin_slug ).'"><a href="'.esc_url( $install_url ).'" data-slug="'.esc_attr( $plugin_slug ).'" class="'.esc_attr( $button_class ).'">'.$button_txt.'</a></p>';
204
- echo '<a class="plugin-detail thickbox open-plugin-details-modal" href="'.esc_url( $detail_link ).'">'.esc_html__( 'Details', 'ftdi' ).'</a>';
205
- echo '</div>';
206
- }
207
 
208
- }
209
  }
210
 
 
211
 
212
- function display_import(){
213
- $nonce = wp_create_nonce( 'ft_demo_import' );
214
- $url = admin_url('admin-ajax.php');
215
- $url = remove_query_arg( array( '_nonce', 'action' ) , $url );
216
-
217
- $current_item = apply_filters( 'ft_demo_import_current_item', false );
218
-
219
- if ( ! $current_item ) {
220
- $current_item = get_option( 'template' );
221
- $current_item = untrailingslashit( $current_item );
222
  }
223
 
224
- $current_item_name = str_replace( array( '-', '_' ), ' ', $current_item );
225
- $current_item_name = ucwords( $current_item_name );
226
-
227
-
228
- $import_url = add_query_arg( array(
229
- '_nonce' => $nonce,
230
- 'action' => 'ft_demo_import_content',
231
- 'data' => $current_item
232
- ), $url );
233
-
234
- $download_url = add_query_arg( array(
235
- '_nonce' => $nonce,
236
- 'action' => 'ft_demo_import_download',
237
- 'data' => $current_item
238
- ), $url );
239
-
240
- $import_export_config_url = add_query_arg( array(
241
- '_nonce' => $nonce,
242
- 'action' => 'ft_demo_export',
243
- 'type' => 'config',
244
- 'data' => $current_item
245
- ), $url );
246
-
247
- $this->js();
248
-
249
- $recommend_plugins = $this->get_recommend_plugins();
250
-
251
- ?>
252
- <div class="ft-import-box ft-import-welcome">
253
- <h3><?php esc_html_e( 'Welcome to FameThemes Demo Importer!', 'ftdi' ); ?></h3>
254
- <p>
255
- <?php esc_html_e( 'Importing demo data (post, pages, images, theme settings, ...) is the easiest way to setup your theme. It will allow you to quickly edit everything instead of creating content from scratch. When you import the data, the following things might happen:', 'ftdi' ); ?>
256
- </p>
257
- <ul>
258
- <li><?php esc_html_e( 'No existing posts, pages, categories, images, custom post types or any other data will be deleted or modified.', 'ftdi' ); ?></li>
259
- <li><?php esc_html_e( 'Posts, pages, images, widgets and menus will get imported.', 'ftdi' ); ?></li>
260
- <li><?php esc_html_e( 'Please click "Import Demo Data" button only once and wait, it can take a couple of minutes.', 'ftdi' ); ?></li>
261
- </ul>
262
- <p><?php esc_html_e( 'Notice: If your site already has content, please make sure you backup your database and WordPress files before import demo data.', 'ftdi' ); ?></p>
263
- </div>
264
-
265
- <?php if ( ! empty( $recommend_plugins ) && $this->has_inactive_recommend_plugins( $recommend_plugins ) ) { ?>
266
- <div id="plugin-filter" class="recommend-plugins">
267
- <h3><?php esc_html_e( 'Recommend Plugins', 'ftdi' ); ?></h3>
268
- <p><?php esc_html_e( 'To fully import demo content, please install and activate all recommend plugins before import.', 'ftdi' ); ?></p>
269
- <?php
270
- $this->render_recommend_plugins();
271
- ?>
272
- </div>
273
- <?php } ?>
274
-
275
- <?php if ( $this->check_data_exists( $current_item ) ){ ?>
276
- <div class="ft-import-box ft-import-theme">
277
- <p><?php printf( esc_html__( 'You are ready to import demo data for %1$s', 'ftdi' ), '<strong>'.esc_html( $current_item_name ).'</strong>' ); ?></p>
278
- </div>
279
-
280
- <div class="ft-import-action">
281
- <a class="button button-primary button-hero ft-demo-import-now" data-download="<?php echo esc_url( $download_url ); ?>" data-import="<?php echo esc_url( $import_url ); ?>" href="#"><?php esc_html_e( 'Import Demo Data', 'ftdi' ); ?></a>
282
- <?php if ( isset( $_REQUEST['export'] ) ) { ?>
283
- <a class="button button-secondary ft-export-config" href="<?php echo esc_url( $import_export_config_url ); ?>"><?php esc_html_e( 'Export Config', 'ftdi' ); ?></a>
284
- <?php } ?>
285
- </div>
286
- <div class="ft-ajax-notice"></div>
287
-
288
- <?php } else { ?>
289
- <div class="ft-import-box ft-import-theme">
290
- <p><strong><?php esc_html_e( 'Notice:', 'ftdi' ); ?></strong> <?php esc_html_e( "No FameThemes's themes data detected, please make sure you are using one of our theme.", 'ftdi' ); ?></p>
291
- </div>
292
- <?php } ?>
293
- <?php
294
-
295
- }
296
-
297
- function js(){
298
- wp_enqueue_script( 'ft-demo-importer', $this->url.'assets/js/importer.js', array( 'jquery' ) );
299
- wp_localize_script( 'ft-demo-importer', 'FT_IMPORT_DEMO', array(
300
- 'downloading' => esc_html__( 'Downloading...', 'ftid' ),
301
- 'importing' => esc_html__( 'Importing...', 'ftid' ),
302
- 'import' => esc_html__( 'Import Now', 'ftid' ),
303
- 'import_again' => esc_html__( 'Import Again.', 'ftid' ),
304
- 'imported' => esc_html__( 'Demo Data Imported !', 'ftid' ),
305
- 'import_error' => esc_html__( 'Demo Data Import Error', 'ftid' ),
306
- 'import_error_msg' => sprintf( esc_html__( 'Check your %1$s, demo data may imported.', 'ftid' ), '<a target="blank" href="'.home_url('/').'">'.esc_html__( 'front end', 'ftid' ).'</a>' ),
307
- 'confirm_import' => esc_html__( 'Are you sure ?', 'ftid' ),
308
- 'confirm_leave' => esc_html__( 'Importing script is running, do you want to stop it ?', 'ftid' ),
309
- 'demo_imported' => sprintf( esc_html__( 'The demo import has finished. Please check your %1$s and make sure that everything has imported correctly. If it did, you can deactivate the FameThemes Demo Importer plugin, because it has done its job.', 'ftdi' ),
310
- '<a target="_blank" href="'.esc_url( home_url( '/' ) ).'">'.esc_html__( 'front page', 'ftdi' ).'</a>' ),
311
- 'no_data_found' => esc_html__( 'No data found.', 'ftid' ),
312
- 'demo_import_failed' => sprintf( esc_html__( 'Demo data import failed, please %1$s to get help.', 'ftdi' ), '<a target="_blank" href="https://www.famethemes.com/contact">'.esc_html__( 'contact us', 'ftdi' ).'</a>' ),
313
-
314
- ) );
315
  }
316
 
317
- function css( ){
318
- wp_enqueue_style( 'ft-demo-importer', $this->url . 'assets/css/importer.css', false );
319
  }
320
 
321
- function check_data_exists( $item_name ){
322
- $file = $this->git_repo.$item_name.'/dummy-data.xml';
323
- $response = wp_remote_get( $file );
324
- $response_code = wp_remote_retrieve_response_code( $response );
325
- if ( 200 != $response_code ) {
326
- return false;
327
- }
328
-
329
- return true;
330
- }
331
 
332
  /**
333
  * Handles a side-loaded file in the same way as an uploaded file is handled by media_handle_upload().
@@ -412,6 +133,8 @@ class FT_Demo_Content_Importer {
412
 
413
  if ( $name ) {
414
  $file_array['name'] = $name;
 
 
415
  }
416
  // Do the validation and storage stuff.
417
  $file_path_or_id = self::media_handle_sideload( $file_array, 0, null, array(), $save_attachment );
@@ -421,107 +144,298 @@ class FT_Demo_Content_Importer {
421
  @unlink( $file_array['tmp_name'] );
422
  return false;
423
  }
424
-
425
  return $file_path_or_id;
426
  }
427
 
428
- function download_dummy_files( $item_name ){
429
- $files = array(
430
- 'dummy-data' => 'xml', // file ext
431
- 'config' => 'json'
432
- );
433
- $downloaded_file = array();
434
- foreach ( $files as $k => $ext ) {
435
- $file = $item_name.'-'.$k.'.'.$ext;
436
- $file_path = self::download_file( $this->git_repo.$item_name.'/'.$k.'.'.$ext, $file, false );
437
- $downloaded_file[ $k ] = $file_path;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  }
439
 
440
- return $downloaded_file;
 
441
  }
442
 
443
 
 
444
 
445
- function data_dir( $param ){
 
 
 
 
 
 
 
 
446
 
447
- $siteurl = get_option( 'siteurl' );
448
- $upload_path = trim( get_option( 'upload_path' ) );
 
 
 
 
 
 
 
 
 
 
 
 
449
 
450
- if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path ) {
451
- $dir = WP_CONTENT_DIR . '/uploads';
452
- } elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) {
453
- // $dir is absolute, $upload_path is (maybe) relative to ABSPATH
454
- $dir = path_join( ABSPATH, $upload_path );
455
  } else {
456
- $dir = $upload_path;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
  }
458
 
459
- if ( !$url = get_option( 'upload_url_path' ) ) {
460
- if ( empty($upload_path) || ( 'wp-content/uploads' == $upload_path ) || ( $upload_path == $dir ) )
461
- $url = WP_CONTENT_URL . '/uploads';
462
- else
463
- $url = trailingslashit( $siteurl ) . $upload_path;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  }
465
 
 
 
 
 
466
 
467
- $param['path'] = $dir . '/ft-dummy-data';
468
- $param['url'] = $url . '/ft-dummy-data';
 
 
469
 
470
- return $param;
471
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
472
 
 
473
 
474
- function ajax_download(){
475
- $nonce = $_REQUEST['_nonce'];
476
- if ( ! wp_verify_nonce( $nonce, 'ft_demo_import' ) ) {
477
- die( 'Security check' );
478
  }
479
- add_filter('upload_dir', array( $this, 'data_dir' ), 99 );
480
 
481
- $item = wp_strip_all_tags( $_REQUEST['data'] );
482
- delete_transient( 'ft_demo_import_downloaded_'.$item );
483
- $import_files = $this->download_dummy_files( $item );
484
- set_transient( 'ft_demo_import_downloaded_'.$item, $import_files, 3 * HOUR_IN_SECONDS );
485
 
486
- remove_filter('upload_dir', array( $this, 'data_dir' ), 99 );
 
487
 
488
- wp_die('downloaded');
489
  }
490
 
491
- function ajax_import(){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
 
493
- $nonce = $_REQUEST['_nonce'];
494
- if ( ! wp_verify_nonce( $nonce, 'ft_demo_import' ) ) {
495
- die( 'Security check' );
496
  }
497
 
498
- $item = wp_strip_all_tags( $_REQUEST['data'] );
499
- $import_files = get_transient( 'ft_demo_import_downloaded_'.$item );
500
 
501
- if ( $import_files ) {
502
- $import = new FT_Demo_Content( $import_files );
503
- $import->import();
504
- } else {
505
- echo 'no_data_found';
 
 
 
 
 
 
506
  }
 
507
 
508
- // Remove data files
509
- /*
510
- foreach ( $import_files as $k => $f ) {
511
- if ( file_exists( $f ) ) {
512
- @unlink( $f );
513
  }
 
 
 
 
 
 
 
514
  }
515
- */
516
 
517
- die();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  }
519
 
 
 
 
 
 
 
 
520
 
521
- function ajax_export(){
522
- $type = $_REQUEST['type'];
523
  ob_start();
524
  ob_end_clean();
 
525
 
526
  /**
527
  * Filters the export filename.
@@ -538,27 +452,40 @@ class FT_Demo_Content_Importer {
538
  header( 'Content-Disposition: attachment; filename=' . $filename );
539
  header( 'Content-Type: application/xml; charset=' . get_option( 'blog_charset' ), true );
540
 
541
- switch ( $type ) {
542
- case 'options':
543
- case 'option':
544
- case 'config':
545
- case 'widget':
546
- case 'widgets':
547
- case 'theme_mod':
548
- case 'theme_mods':
549
- echo FT_Demo_Content::generate_config();
550
- break;
551
- }
552
  die();
553
  }
554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
  }
556
 
 
 
 
 
 
 
557
  if ( is_admin() ) {
558
- function ft_demo_importer(){
559
- new FT_Demo_Content_Importer();
 
560
  }
561
- add_action( 'plugins_loaded', 'ft_demo_importer' );
562
  }
563
 
564
  /**
@@ -567,8 +494,9 @@ if ( is_admin() ) {
567
  * @param $plugin
568
  * @param bool|false $network_wide
569
  */
570
- function ft_demo_importer_plugin_activate( $plugin, $network_wide = false ) {
571
  if ( ! $network_wide && $plugin == plugin_basename( __FILE__ ) ) {
 
572
  $template_slug = get_option('template');
573
  $url = add_query_arg(
574
  array(
@@ -577,9 +505,46 @@ function ft_demo_importer_plugin_activate( $plugin, $network_wide = false ) {
577
  ),
578
  admin_url('themes.php')
579
  );
580
- wp_redirect($url);
581
- die();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
  }
583
  }
584
- add_action( 'activated_plugin', 'ft_demo_importer_plugin_activate', 90, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
 
5
  Description: Demo data import tool for FameThemes's themes.
6
  Author: famethemes
7
  Author URI: http://www.famethemes.com/
8
+ Version: 1.0.7
9
+ Text Domain: demo-contents
10
  License: GPL version 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
  */
12
 
13
 
14
+ define( 'DEMO_CONTENT_URL', trailingslashit ( plugins_url('', __FILE__) ) );
15
+ define( 'DEMO_CONTENT_PATH', trailingslashit( plugin_dir_path( __FILE__) ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ class Demo_Contents {
19
+ public $dir;
20
+ public $url;
21
+ private static $git_repo = 'FameThemes/famethemes-xml-demos';
22
+ public $dashboard;
23
+ public $progress;
24
+ static $_instance;
25
+
26
+ static function get_instance(){
27
+ if ( is_null( self::$_instance ) ) {
28
+ self::$_instance = new self();
29
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ return self::$_instance ;
32
  }
33
 
34
+ function __construct(){
35
 
36
+ require_once DEMO_CONTENT_PATH.'inc/class-tgm-plugin-activation.php';
37
+ require_once DEMO_CONTENT_PATH.'inc/theme-supports.php';
38
+ require_once DEMO_CONTENT_PATH.'inc/class-dashboard.php';
39
+ require_once DEMO_CONTENT_PATH.'inc/class-progress.php';
40
+ $this->dashboard = new Demo_Content_Dashboard();
41
+ $this->progress = new Demo_Contents_Progress();
42
+ if ( is_admin() ) {
43
+ add_action('init', array($this, 'export'));
 
 
44
  }
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
 
48
+ static function get_github_repo(){
49
+ return apply_filters( 'demo_contents_github_repo', self::$git_repo );
50
  }
51
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  /**
54
  * Handles a side-loaded file in the same way as an uploaded file is handled by media_handle_upload().
133
 
134
  if ( $name ) {
135
  $file_array['name'] = $name;
136
+ } else {
137
+ $file_array['name'] = basename( $url );
138
  }
139
  // Do the validation and storage stuff.
140
  $file_path_or_id = self::media_handle_sideload( $file_array, 0, null, array(), $save_attachment );
144
  @unlink( $file_array['tmp_name'] );
145
  return false;
146
  }
 
147
  return $file_path_or_id;
148
  }
149
 
150
+ /**
151
+ * Available widgets
152
+ *
153
+ * Gather site's widgets into array with ID base, name, etc.
154
+ * Used by export and import functions.
155
+ *
156
+ * @since 0.4
157
+ * @global array $wp_registered_widget_updates
158
+ * @return array Widget information
159
+ */
160
+ static function get_available_widgets() {
161
+
162
+ global $wp_registered_widget_controls;
163
+
164
+ $widget_controls = $wp_registered_widget_controls;
165
+
166
+ $available_widgets = array();
167
+
168
+ foreach ( $widget_controls as $widget ) {
169
+
170
+ if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[$widget['id_base']] ) ) { // no dupes
171
+
172
+ $available_widgets[$widget['id_base']]['id_base'] = $widget['id_base'];
173
+ $available_widgets[$widget['id_base']]['name'] = $widget['name'];
174
+
175
+ }
176
+
177
  }
178
 
179
+ return $available_widgets;
180
+
181
  }
182
 
183
 
184
+ static function get_update_keys(){
185
 
186
+ $key = 'demo_contents_customizer_keys';
187
+ $theme_slug = get_option( 'stylesheet' );
188
+ $data = get_option( $key );
189
+ if ( ! is_array( $data ) ) {
190
+ $data = array();
191
+ }
192
+ if ( isset( $data[ $theme_slug ] ) ){
193
+ return $data[ $theme_slug ];
194
+ }
195
 
196
+ $r = wp_remote_post( admin_url( 'customize.php' ), array(
197
+ 'method' => 'POST',
198
+ 'timeout' => 45,
199
+ 'redirection' => 5,
200
+ 'httpversion' => '1.0',
201
+ 'blocking' => true,
202
+ 'headers' => array(),
203
+ 'cookies' => array(
204
+ SECURE_AUTH_COOKIE => $_COOKIE[ SECURE_AUTH_COOKIE ],
205
+ AUTH_COOKIE => $_COOKIE[ AUTH_COOKIE ],
206
+ LOGGED_IN_COOKIE => $_COOKIE[ LOGGED_IN_COOKIE ],
207
+ )
208
+ )
209
+ );
210
 
211
+ if ( is_wp_error( $r ) ) {
212
+ return false;
 
 
 
213
  } else {
214
+ global $wpdb;
215
+
216
+ $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $key ) );
217
+ $notoptions = wp_cache_get( 'notoptions', 'options' );
218
+ // Has to be get_row instead of get_var because of funkiness with 0, false, null values
219
+ if ( is_object( $row ) ) {
220
+ $value = $row->option_value;
221
+ $data = apply_filters( 'option_' . $key, maybe_unserialize( $value ), $key );
222
+ wp_cache_add( $key, $value, 'options' );
223
+ } else { // option does not exist, so we must cache its non-existence
224
+ if ( ! is_array( $notoptions ) ) {
225
+ $notoptions = array();
226
+ }
227
+ $notoptions[$key] = true;
228
+ wp_cache_set( 'notoptions', $notoptions, 'options' );
229
+
230
+ /** This filter is documented in wp-includes/option.php */
231
+ $data = apply_filters( 'default_option_' . $key, '', $key );
232
+ }
233
+
234
+ if ( ! is_array( $data ) ) {
235
+ $data = array();
236
+ }
237
+
238
+ if ( isset( $data[ $theme_slug ] ) ) {
239
+ return $data[ $theme_slug ];
240
+ }
241
  }
242
 
243
+ return false;
244
+ }
245
+
246
+ /**
247
+ * Generate Widgets export data
248
+ *
249
+ * @since 0.1
250
+ * @return string Export file contents
251
+ */
252
+ static function generate_widgets_export_data() {
253
+
254
+ // Get all available widgets site supports
255
+ $available_widgets = self::get_available_widgets();
256
+
257
+ // Get all widget instances for each widget
258
+ $widget_instances = array();
259
+ foreach ( $available_widgets as $widget_data ) {
260
+
261
+ // Get all instances for this ID base
262
+ $instances = get_option( 'widget_' . $widget_data['id_base'] );
263
+
264
+ // Have instances
265
+ if ( ! empty( $instances ) ) {
266
+
267
+ // Loop instances
268
+ foreach ( $instances as $instance_id => $instance_data ) {
269
+
270
+ // Key is ID (not _multiwidget)
271
+ if ( is_numeric( $instance_id ) ) {
272
+ $unique_instance_id = $widget_data['id_base'] . '-' . $instance_id;
273
+ $widget_instances[$unique_instance_id] = $instance_data;
274
+ }
275
+
276
+ }
277
+
278
+ }
279
+
280
  }
281
 
282
+ // Gather sidebars with their widget instances
283
+ $sidebars_widgets = get_option( 'sidebars_widgets' ); // get sidebars and their unique widgets IDs
284
+ $sidebars_widget_instances = array();
285
+ foreach ( $sidebars_widgets as $sidebar_id => $widget_ids ) {
286
 
287
+ // Skip inactive widgets
288
+ if ( 'wp_inactive_widgets' == $sidebar_id ) {
289
+ continue;
290
+ }
291
 
292
+ // Skip if no data or not an array (array_version)
293
+ if ( ! is_array( $widget_ids ) || empty( $widget_ids ) ) {
294
+ continue;
295
+ }
296
+
297
+ // Loop widget IDs for this sidebar
298
+ foreach ( $widget_ids as $widget_id ) {
299
+
300
+ // Is there an instance for this widget ID?
301
+ if ( isset( $widget_instances[$widget_id] ) ) {
302
+
303
+ // Add to array
304
+ $sidebars_widget_instances[$sidebar_id][$widget_id] = $widget_instances[$widget_id];
305
+
306
+ }
307
 
308
+ }
309
 
 
 
 
 
310
  }
 
311
 
312
+ // Filter pre-encoded data
313
+ $data = apply_filters( 'ft_demo_export_widgets_data', $sidebars_widget_instances );
 
 
314
 
315
+ // Encode the data for file contents
316
+ return $data;
317
 
 
318
  }
319
 
320
+ static function get_widgets_config_fields( $config){
321
+ $_config = array();
322
+ foreach ( $config as $k => $f ) {
323
+ switch ( $f['type'] ) {
324
+ case 'list_cat':
325
+ $_config[ $f['name'] ] = array(
326
+ 'type' => 'term',
327
+ 'tax' => 'category'
328
+ );
329
+ break;
330
+ case 'group':
331
+ foreach ( $f['fields'] as $_k => $_f ) {
332
+ if ( $_f['type'] == 'source' ) {
333
+ if ( isset( $_f['source']['post_type'] ) ) {
334
+ $_config[ $_f['name'] ] = 'post';
335
+ } else {
336
+ $_config[ $_f['name'] ] = array(
337
+ 'type' => 'term',
338
+ 'tax' => $_f['source']['tax']
339
+ );
340
+ }
341
+ } else {
342
+ $_config[ $_f['name'] ] = $_f['type'];
343
+ }
344
+ }
345
+
346
+ $_config[ $f['name'] ] = 'repeater';
347
+ break;
348
+ default:
349
+ if ( $f['type'] == 'source' ) {
350
+ if ( isset( $f['source']['post_type'] ) ) {
351
+ $_config[ $f['name'] ] = 'post';
352
+ } else {
353
+ $_config[ $f['name'] ] = array(
354
+ 'type' => 'term',
355
+ 'tax' => $f['source']['tax']
356
+ );
357
+ }
358
+ } else {
359
+ $_config[ $f['name'] ] = $f['type'];
360
+ }
361
+ break;
362
+ }
363
 
 
 
 
364
  }
365
 
366
+ return $_config;
367
+ }
368
 
369
+
370
+ static function get_widgets_config(){
371
+ global $wp_registered_widget_controls;
372
+
373
+ $widget_instances = array();
374
+
375
+ foreach ( $wp_registered_widget_controls as $widget_id => $widget ) {
376
+ $base_id = isset($widget['id_base']) ? $widget['id_base'] : null;
377
+ if (!empty($base_id) && !isset($widget_instances[$base_id])) {
378
+ $widget_instances[$base_id] = '';
379
+ }
380
  }
381
+ global $wp_widget_factory;
382
 
383
+ foreach ( $wp_widget_factory->widgets as $class_name => $object ){
384
+ $config = array();
385
+ if( method_exists( $object,'config' ) ) {
386
+ $config = $object->config();
 
387
  }
388
+ // get_configs
389
+ if( method_exists( $object,'get_configs' ) ) {
390
+ $config = $object->get_configs();
391
+ }
392
+
393
+
394
+ $widget_instances[$object->id_base] = self::get_widgets_config_fields( $config );
395
  }
 
396
 
397
+ return $widget_instances;
398
+ }
399
+
400
+
401
+ static function generate_config(){
402
+ $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
403
+ // Just update the customizer keys
404
+
405
+ $regen_keys = self::get_update_keys();
406
+
407
+ $config = array(
408
+ 'home_url' => home_url('/'),
409
+ 'menus' => $nav_menu_locations,
410
+ 'pages' => array(
411
+ 'page_on_front' => get_option( 'page_on_front' ),
412
+ 'page_for_posts' => get_option( 'page_for_posts' ),
413
+ ),
414
+ 'options' => array(
415
+ 'show_on_front' => get_option( 'show_on_front' )
416
+ ),
417
+ 'theme_mods' => get_theme_mods(),
418
+ 'widgets' => self::generate_widgets_export_data(),
419
+ 'widgets_config' => self::get_widgets_config(),
420
+ 'customizer_keys' => $regen_keys
421
+ );
422
+
423
+ $config = apply_filters( 'demo_contents_generate_config', $config );
424
+
425
+ return json_encode( $config );
426
  }
427
 
428
+ function export(){
429
+ if ( ! isset( $_REQUEST['demo_contents_export'] ) ) {
430
+ return ;
431
+ }
432
+ if ( ! current_user_can( 'export' ) ) {
433
+ return ;
434
+ }
435
 
 
 
436
  ob_start();
437
  ob_end_clean();
438
+ ob_flush();
439
 
440
  /**
441
  * Filters the export filename.
452
  header( 'Content-Disposition: attachment; filename=' . $filename );
453
  header( 'Content-Type: application/xml; charset=' . get_option( 'blog_charset' ), true );
454
 
455
+ echo Demo_Contents::generate_config();
 
 
 
 
 
 
 
 
 
 
456
  die();
457
  }
458
 
459
+ /**
460
+ * Check if an item exists out there in the "ether".
461
+ *
462
+ * @param string $url - preferably a fully qualified URL
463
+ * @return boolean - true if it is out there somewhere
464
+ */
465
+ static function url_exists($url) {
466
+ if (($url == '') || ($url == null)) { return false; }
467
+ $response = wp_remote_head( $url, array( 'timeout' => 5 ) );
468
+ $accepted_status_codes = array( 200, 301, 302 );
469
+ if ( ! is_wp_error( $response ) && in_array( wp_remote_retrieve_response_code( $response ), $accepted_status_codes ) ) {
470
+ return true;
471
+ }
472
+ return false;
473
+ }
474
+
475
  }
476
 
477
+ /*
478
+ echo '<pre>';
479
+ print_r( get_theme_mod( 'clients_items' ) );
480
+ echo '</pre>';
481
+ */
482
+
483
  if ( is_admin() ) {
484
+ function demo_contents__init(){
485
+ new Demo_Contents();
486
+
487
  }
488
+ add_action( 'plugins_loaded', 'demo_contents__init' );
489
  }
490
 
491
  /**
494
  * @param $plugin
495
  * @param bool|false $network_wide
496
  */
497
+ function demo_contents_importer_plugin_activate( $plugin, $network_wide = false ) {
498
  if ( ! $network_wide && $plugin == plugin_basename( __FILE__ ) ) {
499
+
500
  $template_slug = get_option('template');
501
  $url = add_query_arg(
502
  array(
505
  ),
506
  admin_url('themes.php')
507
  );
508
+
509
+ // Check url exists
510
+ if ( Demo_Contents::url_exists( $url ) ) {
511
+ wp_redirect($url);
512
+ die();
513
+ }
514
+
515
+ $url = add_query_arg(
516
+ array(
517
+ 'page' => $template_slug,
518
+ 'tab' => 'demo-data-importer',
519
+ ),
520
+ admin_url('themes.php')
521
+ );
522
+ if ( Demo_Contents::url_exists( $url ) ) {
523
+ wp_redirect($url);
524
+ die();
525
+ }
526
  }
527
  }
528
+ add_action( 'activated_plugin', 'demo_contents_importer_plugin_activate', 90, 2 );
529
+
530
+
531
+ // Support Upload XML file
532
+ add_filter('upload_mimes', 'demo_contents_custom_upload_xml');
533
+ function demo_contents_custom_upload_xml($mimes)
534
+ {
535
+ if ( current_user_can( 'upload_files' ) ) {
536
+ $mimes = array_merge($mimes, array(
537
+ 'xml' => 'application/xml',
538
+ 'json' => 'application/json'
539
+ ));
540
+ }
541
+ return $mimes;
542
+ }
543
+
544
+
545
+
546
+
547
+
548
+
549
+
550
 
inc/class-dashboard.php ADDED
@@ -0,0 +1,686 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Demo_Content_Dashboard {
3
+ private $api_url = 'https://www.famethemes.com/wp-json/wp/v2/download/?download_type=15&per_page=100&orderby=title&order=asc';
4
+ private $errors = array();
5
+ // private $cache_time = 3*HOUR_IN_SECONDS;
6
+ private $cache_time = 0;
7
+ private $page_slug = 'demo-contents';
8
+ private $config_slugs = array(
9
+ 'coupon-wp' => 'wp-coupon'
10
+ );
11
+ private $items = array();
12
+ private $allowed_authors = array();
13
+ public $tgmpa = null;
14
+ public $is_theme_page = false;
15
+ function __construct()
16
+ {
17
+
18
+ add_action( 'admin_menu', array( $this, 'add_menu' ) );
19
+ add_action( 'admin_footer', array( $this, 'preview_template' ) );
20
+ add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
21
+ add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
22
+
23
+ $current_theme_slug = get_option( 'template' );
24
+ $no_pro = false;
25
+ if ( strpos( $current_theme_slug, '-pro' ) !== false ) {
26
+ $no_pro = str_replace( '-pro', '', $current_theme_slug );
27
+ }
28
+
29
+ add_action( $current_theme_slug.'_demo_import_content_tab', array( $this, 'wellcome' ), 10 );
30
+ add_action( 'theme_demo_import_content_tab', array( $this, 'wellcome' ), 10 );
31
+ add_action( $current_theme_slug.'_demo_import_content_tab', array( $this, 'listing_themes' ), 35 );
32
+
33
+ if ( $no_pro ) {
34
+ add_action( $no_pro.'_demo_import_content_tab', array( $this, 'wellcome' ), 10 );
35
+ add_action( $no_pro.'_demo_import_content_tab', array( $this, 'listing_themes' ), 35 );
36
+ }
37
+
38
+ // Remove no pro suffix
39
+ add_action( 'theme_demo_import_content_tab', array( $this, 'listing_themes' ), 35 );
40
+ add_action( 'current_screen', array( $this, 'setup_screen' ) );
41
+
42
+ }
43
+
44
+ function setup_screen(){
45
+ $screen = get_current_screen();
46
+ if ( strpos( $screen->id, 'appearance_' ) !== false ) {
47
+ $this->is_theme_page = true;
48
+ }
49
+ }
50
+
51
+ function get_tgmpa(){
52
+ if ( empty( $this->tgmpa ) ) {
53
+ if ( class_exists( 'TGM_Plugin_Activation' ) ) {
54
+ $this->tgmpa = isset( $GLOBALS['tgmpa'] ) ? $GLOBALS['tgmpa'] : TGM_Plugin_Activation::get_instance();
55
+ }
56
+ }
57
+ return $this->tgmpa;
58
+ }
59
+
60
+
61
+ function scripts( $hook = '' ){
62
+ $load_script = false;
63
+ if ( strpos( $hook, 'appearance_' ) === 0 ) {
64
+ $load_script = true;
65
+ }
66
+
67
+ if ( ! $load_script ) {
68
+ if ( strpos( $hook, 'demo-contents' ) !== false ) {
69
+ $load_script = true;
70
+ }
71
+ }
72
+
73
+ $load_script = apply_filters( 'demo_contents_load_scripts', $load_script, $hook );
74
+ if ( ! $load_script ) {
75
+ return false;
76
+ }
77
+
78
+ wp_enqueue_style( 'demo-contents', DEMO_CONTENT_URL . 'style.css', false );
79
+ wp_enqueue_script( 'underscore');
80
+ wp_enqueue_script( 'demo-contents', DEMO_CONTENT_URL.'assets/js/importer.js', array( 'jquery', 'underscore' ) );
81
+ wp_enqueue_media();
82
+ $run = isset( $_REQUEST['import_now'] ) && $_REQUEST['import_now'] == 1 ? 'run' : 'no';
83
+ $themes = $this->setup_themes( $this->is_theme_page ? false : true );
84
+ $tgm_url = '';
85
+ // Localize the javascript.
86
+ $plugins = array();
87
+ $this->get_tgmpa();
88
+ if ( ! empty( $this->tgmpa ) ) {
89
+ $tgm_url = $this->tgmpa->get_tgmpa_url();
90
+ $plugins = $this->get_tgmpa_plugins();
91
+ }
92
+
93
+ $template_slug = get_option( 'template' );
94
+ $theme_slug = get_option( 'stylesheet' );
95
+
96
+ wp_localize_script( 'demo-contents', 'demo_contents_params', array(
97
+ 'tgm_plugin_nonce' => array(
98
+ 'update' => wp_create_nonce( 'tgmpa-update' ),
99
+ 'install' => wp_create_nonce( 'tgmpa-install' ),
100
+ ),
101
+ 'messages' => array(
102
+ 'plugin_installed' => __( '%s installed', 'demo-contents' ),
103
+ 'plugin_not_installed' => __( '%s not installed', 'demo-contents' ),
104
+ 'plugin_not_activated' => __( '%s not activated', 'demo-contents' ),
105
+ 'plugin_installing' => __( 'Installing %s...', 'demo-contents' ),
106
+ 'plugin_activating' => __( 'Activating %s...', 'demo-contents' ),
107
+ 'plugin_activated' => __( '%s activated', 'demo-contents' ),
108
+ ),
109
+ 'tgm_bulk_url' => $tgm_url,
110
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
111
+ 'theme_url' => admin_url( 'themes.php' ),
112
+ 'wpnonce' => wp_create_nonce( 'merlin_nonce' ),
113
+ 'action_install_plugin' => 'tgmpa-bulk-activate',
114
+ 'action_active_plugin' => 'tgmpa-bulk-activate',
115
+ 'action_update_plugin' => 'tgmpa-bulk-update',
116
+ 'plugins' => $plugins,
117
+ 'home' => home_url('/'),
118
+ 'btn_done_label' => __( 'All Done! View Site', 'demo-contents' ),
119
+ 'failed_msg' => __( 'Import Failed!', 'demo-contents' ),
120
+ 'import_now' => __( 'Import Now', 'demo-contents' ),
121
+ 'importing' => __( 'Importing...', 'demo-contents' ),
122
+ 'activate_theme' => __( 'Activate Now', 'demo-contents' ),
123
+ 'checking_theme' => __( 'Checking theme', 'demo-contents' ),
124
+ 'checking_resource' => __( 'Checking resource', 'demo-contents' ),
125
+ 'confirm_leave' => __( 'Importing demo content..., are you sure want to cancel ?', 'demo-contents' ),
126
+ 'installed_themes' => $themes,
127
+ 'current_theme' => $template_slug,
128
+ 'current_child_theme' => $theme_slug,
129
+ ) );
130
+
131
+ }
132
+
133
+ /**
134
+ * Get registered TGMPA plugins
135
+ *
136
+ * @return array
137
+ */
138
+ public function get_tgmpa_plugins() {
139
+ $this->get_tgmpa();
140
+ if ( empty( $this->tgmpa ) ) {
141
+ return array();
142
+ }
143
+ $plugins = array(
144
+ 'all' => array(), // Meaning: all plugins which still have open actions.
145
+ 'install' => array(),
146
+ 'update' => array(),
147
+ 'activate' => array(),
148
+ );
149
+
150
+ $tgmpa_url = $this->tgmpa->get_tgmpa_url();
151
+
152
+ foreach ( $this->tgmpa->plugins as $slug => $plugin ) {
153
+ if ( $this->tgmpa->is_plugin_active( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
154
+ continue;
155
+ } else {
156
+ $plugins['all'][ $slug ] = $plugin;
157
+
158
+ $args = array(
159
+ 'plugin' => $slug,
160
+ 'tgmpa-page' => $this->tgmpa->menu,
161
+ 'plugin_status' => 'all',
162
+ '_wpnonce' => wp_create_nonce('bulk-plugins'),
163
+ 'action' => '',
164
+ 'action2' => -1,
165
+ //'message' => esc_html__('Installing', '@@textdomain'),
166
+ );
167
+
168
+ $plugin['page_url'] = $tgmpa_url;
169
+
170
+ if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
171
+ $plugins['install'][ $slug ] = $plugin;
172
+ $action = 'tgmpa-bulk-install';
173
+ $args['action'] = $action;
174
+ $plugins['install'][ $slug ][ 'args' ] = $args;
175
+ } else {
176
+ if ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
177
+ $plugins['update'][ $slug ] = $plugin;
178
+ $action = 'tgmpa-bulk-update';
179
+ $args['action'] = $action;
180
+ $plugins['update'][ $slug ][ 'args' ] = $args;
181
+ }
182
+ if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
183
+ $plugins['activate'][ $slug ] = $plugin;
184
+ $action = 'tgmpa-bulk-activate';
185
+ $args['action'] = $action;
186
+ $plugins['activate'][ $slug ][ 'args' ] = $args;
187
+ }
188
+ }
189
+
190
+ }
191
+ }
192
+
193
+ return $plugins;
194
+ }
195
+
196
+
197
+ function add_menu() {
198
+ add_management_page( __( 'Demo Contents', 'demo-contents' ), __( 'Demo Contents', 'demo-contents' ), 'manage_options', $this->page_slug, array( $this, 'dashboard' ) );
199
+ }
200
+
201
+ function get_allowed_authors(){
202
+ if ( empty( $this->allowed_authors ) ) {
203
+ $this->allowed_authors = apply_filters( 'demo_contents_allowed_authors', array(
204
+ 'famethemes' => 'FameThemes',
205
+ 'daisy themes' => 'Daisy Themes'
206
+ ) );
207
+ }
208
+ return $this->allowed_authors;
209
+ }
210
+
211
+ function is_allowed_theme( $author ){
212
+ $allowed = false;
213
+ if ( $author ) {
214
+ $author = strtolower( sanitize_text_field( $author ) );
215
+ $authors = $this->get_allowed_authors();
216
+ $allowed = isset( $authors[ $author ] ) ? true : false;
217
+ }
218
+
219
+ return apply_filters( 'demo_content_is_allowed_author', $allowed, $author );
220
+ }
221
+
222
+ function get_default_author_name(){
223
+ return apply_filters( 'demo_content_default_author', 'FameThemes' );
224
+ }
225
+
226
+ function get_items( $all_demos = true ){
227
+ return $this->setup_themes( $all_demos );
228
+ }
229
+
230
+ function is_installed( $theme_slug ){
231
+ $check = wp_get_theme( $theme_slug );
232
+ return $check->exists();
233
+ }
234
+
235
+ function preview_template(){
236
+ ?>
237
+ <script id="tmpl-demo-contents--preview" type="text/html">
238
+ <div id="demo-contents--preview">
239
+
240
+ <span type="button" class="demo-contents-collapse-sidebar button" aria-expanded="true">
241
+ <span class="collapse-sidebar-arrow"></span>
242
+ <span class="collapse-sidebar-label"><?php _e( 'Collapse', 'demo-contents' ); ?></span>
243
+ </span>
244
+
245
+ <div id="demo-contents-sidebar">
246
+ <span class="demo-contents-close"><span class="screen-reader-text"><?php _e( 'Close', 'fdi' ); ?></span></span>
247
+
248
+ <div id="demo-contents-sidebar-topbar">
249
+ <span class="ft-theme-name">{{ data.name }}</span>
250
+ </div>
251
+
252
+ <div id="demo-contents-sidebar-content">
253
+ <# if ( data.demo_version ) { #>
254
+ <div id="demo-contents-sidebar-heading">
255
+ <span><?php _e( "Your're viewing demo", 'demo-contents' ); ?></span>
256
+ <strong class="panel-title site-title">{{ data.demo_name }}</strong>
257
+ </div>
258
+ <# } #>
259
+ <# if ( data.img ) { #>
260
+ <div class="demo-contents--theme-thumbnail"><img src="{{ data.img }}" alt=""/></div>
261
+ <# } #>
262
+
263
+ <div class="demo-contents--activate-notice">
264
+ <?php _e( 'This theme is inactivated. Your must activate this theme before import demo content', 'demo-contents' ); ?>
265
+ </div>
266
+
267
+ <div class="demo-contents--activate-notice resources-not-found demo-contents-hide">
268
+ <p class="demo-contents--msg"></p>
269
+ <div class="demo-contents---upload">
270
+ <p><button type="button" class="demo-contents--upload-xml button-secondary"><?php _e( 'Upload XML file .xml', 'demo-contents' ); ?></button></p>
271
+ <p><button type="button" class="demo-contents--upload-json button-secondary"><?php _e( 'Upload config file .json or .txt', 'demo-contents' ); ?></button></p>
272
+ </div>
273
+ </div>
274
+
275
+ <div class="demo-contents-import-progress">
276
+
277
+ <div class="demo-contents--step demo-contents-install-plugins demo-contents--waiting">
278
+ <div class="demo-contents--step-heading"><?php _e( 'Install Recommended Plugins', 'demo-contents' ); ?></div>
279
+ <div class="demo-contents--status demo-contents--loading"></div>
280
+ <div class="demo-contents--child-steps"></div>
281
+ </div>
282
+
283
+ <div class="demo-contents--step demo-contents-import-users demo-contents--waiting">
284
+ <div class="demo-contents--step-heading"><?php _e( 'Import Users', 'demo-contents' ); ?></div>
285
+ <div class="demo-contents--status demo-contents--waiting"></div>
286
+ <div class="demo-contents--child-steps"></div>
287
+ </div>
288
+
289
+ <div class="demo-contents--step demo-contents-import-categories demo-contents--waiting">
290
+ <div class="demo-contents--step-heading"><?php _e( 'Import Categories', 'demo-contents' ); ?></div>
291
+ <div class="demo-contents--status demo-contents--completed"></div>
292
+ <div class="demo-contents--child-steps"></div>
293
+ </div>
294
+
295
+ <div class="demo-contents--step demo-contents-import-tags demo-contents--waiting">
296
+ <div class="demo-contents--step-heading"><?php _e( 'Import Tags', 'demo-contents' ); ?></div>
297
+ <div class="demo-contents--status demo-contents--completed"></div>
298
+ <div class="demo-contents--child-steps"></div>
299
+ </div>
300
+
301
+ <div class="demo-contents--step demo-contents-import-taxs demo-contents--waiting">
302
+ <div class="demo-contents--step-heading"><?php _e( 'Import Taxonomies', 'demo-contents' ); ?></div>
303
+ <div class="demo-contents--status demo-contents--waiting"></div>
304
+ <div class="demo-contents--child-steps"></div>
305
+ </div>
306
+
307
+ <div class="demo-contents--step demo-contents-import-posts demo-contents--waiting">
308
+ <div class="demo-contents--step-heading"><?php _e( 'Import Posts & Media', 'demo-contents' ); ?></div>
309
+ <div class="demo-contents--status demo-contents--waiting"></div>
310
+ <div class="demo-contents--child-steps"></div>
311
+ </div>
312
+
313
+ <div class="demo-contents--step demo-contents-import-theme-options demo-contents--waiting">
314
+ <div class="demo-contents--step-heading"><?php _e( 'Import Options', 'demo-contents' ); ?></div>
315
+ <div class="demo-contents--status demo-contents--waiting"></div>
316
+ <div class="demo-contents--child-steps"></div>
317
+ </div>
318
+
319
+ <div class="demo-contents--step demo-contents-import-widgets demo-contents--waiting">
320
+ <div class="demo-contents--step-heading"><?php _e( 'Import Widgets', 'demo-contents' ); ?></div>
321
+ <div class="demo-contents--status demo-contents--waiting"></div>
322
+ <div class="demo-contents--child-steps"></div>
323
+ </div>
324
+
325
+ <div class="demo-contents--step demo-contents-import-customize demo-contents--waiting">
326
+ <div class="demo-contents--step-heading"><?php _e( 'Import Customize Settings', 'demo-contents' ) ?></div>
327
+ <div class="demo-contents--status demo-contents--waiting"></div>
328
+ <div class="demo-contents--child-steps"></div>
329
+ </div>
330
+ </div>
331
+
332
+ </div><!-- /.demo-contents-sidebar-content -->
333
+
334
+ <div id="demo-contents-sidebar-footer">
335
+ <a href="#" " class="demo-contents--import-now button button-primary"><?php _e( 'Import Now', 'demo-contents' ); ?></a>
336
+ </div>
337
+
338
+ </div>
339
+ <div id="demo-contents-viewing">
340
+ <iframe src="{{ data.demoURL }}"></iframe>
341
+ </div>
342
+ </div>
343
+ </script>
344
+ <?php
345
+ }
346
+
347
+ function get_details_link( $theme_slug, $theme_name ) {
348
+ $link = 'https://www.famethemes.com/themes/'.$theme_slug.'/';
349
+ return apply_filters( 'demo_contents_get_details_link', $link, $theme_slug, $theme_name );
350
+ }
351
+
352
+ function setup_themes( $all_demos = true ){
353
+
354
+ $key = 'demo_contents_get_themes_'.( (int ) $all_demos );
355
+
356
+ $cache = wp_cache_get( $key );
357
+ if ( false !== $cache ) {
358
+ $this->items = $cache;
359
+ }
360
+ // If already setup
361
+ if ( ! empty( $this->items) ) {
362
+ // return $this->items;
363
+ }
364
+
365
+ // if have filter for list themes
366
+ $this->items = apply_filters( 'demo_contents_get_themes', array(), $all_demos, $this );
367
+ if ( ! empty( $this->items) ) {
368
+ return $this->items;
369
+ }
370
+
371
+ $current_theme_slug = get_option( 'template' );
372
+ $child_theme_slug = get_option( 'stylesheet' );
373
+
374
+ $current_active_slugs = array( $current_theme_slug );
375
+
376
+ if ( $all_demos ) {
377
+ $installed_themes = wp_get_themes();
378
+ } else {
379
+ $_activate_theme = wp_get_theme( $current_theme_slug );
380
+ $installed_themes = array( );
381
+ $installed_themes[ $current_theme_slug ] = $_activate_theme;
382
+ }
383
+
384
+ $list_themes = array();
385
+
386
+ // Listing installed themes
387
+ foreach ( ( array )$installed_themes as $theme_slug => $theme ) {
388
+ if ( ! $this->is_allowed_theme($theme->get('Author'))) {
389
+ continue;
390
+ }
391
+ $is_activate = false;
392
+ if( $theme_slug == $current_theme_slug || $theme_slug == $child_theme_slug ) {
393
+ $is_activate = true;
394
+ }
395
+ $list_themes[ $theme_slug ] = array(
396
+ 'slug' => $theme_slug,
397
+ 'name' => $theme->get('Name'),
398
+ 'screenshot' => $theme->get_screenshot(),
399
+ 'author' => $theme->get('Author'),
400
+ 'activate' => $is_activate,
401
+ 'demo_version' => '',
402
+ 'demo_url' => 'https://demos.famethemes.com/'.$theme_slug.'/',
403
+ 'demo_version_name' => '',
404
+ 'is_plugin' => false
405
+ );
406
+ }
407
+ $current_theme = false;
408
+ $child_theme = false;
409
+ if ( isset( $list_themes[ $current_theme_slug ] ) ) {
410
+ $current_theme = $list_themes[ $current_theme_slug ];
411
+ unset( $list_themes[ $current_theme_slug ] );
412
+ }
413
+
414
+ if ( isset( $list_themes[ $child_theme_slug ] ) ) {
415
+ $child_theme = $list_themes[ $child_theme_slug ];
416
+ unset( $list_themes[ $child_theme_slug ] );
417
+ }
418
+
419
+ // Move current theme to top
420
+ if ( $current_theme ) {
421
+ $current_theme['activate'] = true;
422
+ $list_themes = array( $current_theme_slug => $current_theme ) + $list_themes;
423
+ }
424
+
425
+ $support_plugins = array(
426
+ 'onepress-plus/onepress-plus.php' => array(
427
+ 'name' => 'OnePress Plus',
428
+ 'slug' => 'onepress-plus',
429
+ 'theme' => 'onepress'
430
+ ),
431
+ 'screenr-plus/screenr-plus.php' => array(
432
+ 'name' => 'Screenr Plus',
433
+ 'slug' => 'screenr-plus',
434
+ 'theme' => 'screenr'
435
+ )
436
+ );
437
+
438
+ // Check if plugin active
439
+ foreach ( $support_plugins as $plugin => $info ) {
440
+ if ( is_plugin_active( $plugin ) ) {
441
+ if ( $current_theme_slug == $info['theme'] || $child_theme_slug == $info['theme'] ) {
442
+ if ( isset( $list_themes[ $info['theme'] ] ) ) {
443
+ $clone = $list_themes[ $info['theme'] ];
444
+ $clone['activate'] = true;
445
+ $clone['name'] = $info['name'];
446
+ $clone['slug'] = $info['slug'];
447
+ $clone['is_plugin'] = true;
448
+ $clone['demo_url'] = 'https://demos.famethemes.com/'.$info['slug'].'/';
449
+ // Move clone theme to top because it need to stay above current theme
450
+ $list_themes = array( $info['slug'] => $clone ) + $list_themes;
451
+ }
452
+ }
453
+ }
454
+ }
455
+
456
+ // Move child theme to top
457
+ if ( $child_theme ) {
458
+ $child_theme['activate'] = true;
459
+ $list_themes = array( $child_theme_slug => $child_theme ) + $list_themes;
460
+ }
461
+
462
+ $child_demos = array();
463
+ foreach ( $list_themes as $slug => $theme ) {
464
+ if ( $theme['activate'] ) {
465
+ $sub_demos = $this->get_sub_demos( $theme['slug'] );
466
+ if ( ! empty( $sub_demos ) ) {
467
+ // $list_themes = $list_themes + $sub_demos;
468
+ $child_demos = $child_demos + $sub_demos;
469
+ }
470
+ }
471
+ }
472
+ if( ! empty( $child_demos ) ) {
473
+ // remove all other demo themes.
474
+ foreach ( $list_themes as $slug => $theme ) {
475
+ if ( ! $theme['activate'] ) {
476
+ unset( $list_themes[ $slug ] );
477
+ }
478
+
479
+ }
480
+
481
+ }
482
+ $list_themes = $list_themes + $child_demos;
483
+
484
+ $this->items = $list_themes;
485
+ wp_cache_set( $key, $this->items );
486
+
487
+ return $this->items;
488
+ }
489
+
490
+
491
+
492
+ function get_sub_demos( $theme_slug ){
493
+
494
+ $repo_name = Demo_Contents::get_github_repo();
495
+ $key = $repo_name.'/'.$theme_slug;
496
+
497
+ if ( $this->cache_time > 0 ) {
498
+ $cache = get_transient($key);
499
+ if (false !== $cache) {
500
+ return $cache;
501
+ }
502
+ }
503
+
504
+
505
+ $url = sprintf( 'https://api.github.com/repos/%1$s/contents/%2$s/demos', $repo_name, $theme_slug );
506
+ //$url = sprintf( 'https://api.github.com/repos/%1$s/contents/%2$s', $repo_name, $theme_slug );
507
+
508
+ $tokens = array(
509
+ 'a3f436cfc47798cbfb2e2e8b0b6d8bf030121c63',
510
+ 'dd7c2be5ce59384fc134cf066f432820fa54bcea',
511
+ '8a66f39f5d064e8b47dd2fe5b990e59d4bc0a7e9'
512
+ );
513
+
514
+ $token_key = array_rand( $tokens );
515
+
516
+ $args = array(
517
+ 'headers' => array(
518
+ 'Authorization' => "token {$tokens[$token_key]}"
519
+ )
520
+ );
521
+
522
+ $demos = array();
523
+
524
+ $res = wp_remote_get( $url, $args );
525
+
526
+ if ( wp_remote_retrieve_response_code( $res ) !== 200 ) {
527
+ set_transient( $key, $demos, $this->cache_time );
528
+ return false;
529
+ }
530
+
531
+ $body = wp_remote_retrieve_body( $res );
532
+ $files = json_decode( $body, true );
533
+ if ( empty( $files ) ) {
534
+ set_transient( $key, $demos, $this->cache_time );
535
+ return false;
536
+ }
537
+
538
+ $repo_username = explode('/', $repo_name );
539
+ $repo_username = $repo_username[0];
540
+ //
541
+
542
+ foreach ( $files as $file ) {
543
+ if ( $file['type'] != 'dir' ) {
544
+ continue;
545
+ }
546
+ $slug = $theme_slug.'-'.$file['name'];
547
+ $name = $file['name'];
548
+ $name = str_replace( '-', ' ', $name );
549
+ $name = ucwords( $name );
550
+ $screenshot_url = 'https://raw.githubusercontent.com/'.$repo_name.'/master/'.$file['path'].'/screenshot.png';
551
+ $arg = array(
552
+ 'slug' => $theme_slug,
553
+ 'name' => ucwords( $theme_slug ),
554
+ 'screenshot' => $screenshot_url,
555
+ 'author' => $repo_username,
556
+ 'activate' => false,
557
+ 'demo_version' => $file['name'],
558
+ 'demo_url' => 'https://demos.famethemes.com/'.$slug.'/',
559
+ 'demo_version_name' => $name,
560
+ 'is_plugin' => false
561
+ );
562
+
563
+ $demos[ $slug ] = apply_filters( 'demo_contents_child_demo_args', $arg );
564
+ }
565
+
566
+ set_transient( $key, $demos, $this->cache_time ); // cache 2 hours
567
+
568
+ return $demos;
569
+ }
570
+
571
+ function dashboard() {
572
+ if ( ! current_user_can( 'manage_options' ) ) {
573
+ wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
574
+ }
575
+
576
+ $this->setup_themes();
577
+ $number_theme = count( $this->items );
578
+ $link_all = '?page='.$this->page_slug;
579
+ $link_current_theme = '?page='.$this->page_slug.'&tab=current_theme';
580
+ $link_export= '?page='.$this->page_slug.'&tab=export';
581
+ $tab = isset( $_GET['tab'] ) ? $_GET['tab'] : '';
582
+
583
+ ob_start();
584
+
585
+
586
+ ?>
587
+ <div class="wrap demo-contents">
588
+ <h1 class="wp-heading-inline"><?php _e( 'Demo Contents', 'demo-contents' ); ?><span class="title-count theme-count"><?php echo $number_theme; ?></span></h1>
589
+ <div class="wp-filter hide-if-no-js">
590
+ <div class="filter-count">
591
+ <span class="count theme-count"><?php echo $number_theme; ?></span>
592
+ </div>
593
+ <ul class="filter-links">
594
+ <li><a href="<?php echo $link_all; ?>" class="<?php echo ( ! $tab ) ? 'current' : ''; ?>"><?php _e( 'All Demos', 'demo-contents' ); ?></a></li>
595
+ </ul>
596
+ </div>
597
+
598
+ <?php $this->listing_themes( true ); ?>
599
+
600
+ </div><!-- /.wrap -->
601
+ <?php
602
+ }
603
+
604
+ function wellcome(){
605
+ ?>
606
+ <div class="demo-contents-import-box demo-contents-import-welcome">
607
+ <h3><?php esc_html_e( 'Welcome to FameThemes Demo Importer!', 'demo-contents' ); ?></h3>
608
+ <p>
609
+ <?php esc_html_e( 'Importing demo data (post, pages, images, theme settings, ...) is the easiest way to setup your theme. It will allow you to quickly edit everything instead of creating content from scratch. When you import the data, the following things might happen:', 'demo-contents' ); ?>
610
+ </p>
611
+ <ul>
612
+ <li><?php esc_html_e( 'No existing posts, pages, categories, images, custom post types or any other data will be deleted or modified.', 'demo-contents' ); ?></li>
613
+ <li><?php esc_html_e( 'Posts, pages, images, widgets and menus will get imported.', 'demo-contents' ); ?></li>
614
+ <li><?php esc_html_e( 'Click "Start Import Demo" to start import, it can take a couple of minutes.', 'demo-contents' ); ?></li>
615
+ </ul>
616
+ <p><?php esc_html_e( 'Notice: If your site already has content, please make sure you backup your database and WordPress files before import demo data.', 'demo-contents' ); ?></p>
617
+ </div>
618
+ <?php
619
+ }
620
+
621
+ function loop_theme( $theme ){
622
+ ?>
623
+ <div class="theme <?php echo ( $theme['activate'] ) ? 'demo-contents--current-theme' : ''; ?>" tabindex="0">
624
+ <div class="theme-screenshot">
625
+ <img src="<?php echo esc_url($theme['screenshot']); ?>" alt="">
626
+ </div>
627
+ <?php if ( $theme['activate'] ) { ?>
628
+ <span class="more-details"><?php _e('Current Theme', 'demo-contents'); ?></span>
629
+ <?php } else { ?>
630
+ <span class="more-details"><?php _e('View Details', 'demo-contents'); ?></span>
631
+ <?php } ?>
632
+
633
+ <div class="theme-author"><?php sprintf(__('by %s', 'demo-contents'),$theme['author'] ); ?></div>
634
+ <div class="theme-name"><?php echo ( $theme['demo_version_name'] ) ? esc_html( $theme['demo_version_name'] ) : esc_html($theme['name']); ?></div>
635
+ <div class="theme-actions">
636
+ <a
637
+ data-theme-slug="<?php echo esc_attr( $theme['slug'] ); ?>"
638
+ data-demo-version="<?php echo esc_attr( $theme['demo_version'] ); ?>"
639
+ data-demo-version-name="<?php echo esc_attr( $theme['demo_version_name'] ); ?>"
640
+ data-name="<?php echo esc_html($theme['name']); ?>"
641
+ data-demo-url="<?php echo esc_attr( $theme['demo_url'] ); ?>"
642
+ class="demo-contents--preview-theme-btn button button-primary customize"
643
+ href="#"
644
+ ><?php _e('Start Import', 'demo-contents'); ?></a>
645
+ </div>
646
+ </div>
647
+ <?php
648
+ }
649
+
650
+ /**
651
+ * Listing themes
652
+ *
653
+ * @param bool $all_demos true if list all demos of authors, false if listing demos for current theme only
654
+ */
655
+ function listing_themes( $all_demos = false ){
656
+ $this->setup_themes( $all_demos, true );
657
+ $number_theme = count( $this->items );
658
+ ?>
659
+ <div class="theme-browser rendered demo-contents-themes-listing">
660
+ <div class="themes wp-clearfix">
661
+ <?php
662
+ if ( $number_theme > 0 ) {
663
+ if ( has_action( 'demo_contents_before_themes_listing' ) ) {
664
+ do_action( 'demo_contents_before_themes_listing', $this );
665
+ } else {
666
+
667
+ // Listing installed themes
668
+ foreach (( array ) $this->items as $theme_slug => $theme ) {
669
+ $this->loop_theme( $theme );
670
+ }
671
+
672
+ do_action('demo_content_themes_listing');
673
+ } // end check if has actions
674
+ } else {
675
+ ?>
676
+ <div class="demo-contents-no-themes">
677
+ <?php _e( 'No Themes Found', 'demo-contents' ); ?>
678
+ </div>
679
+ <?php
680
+ }
681
+ ?>
682
+ </div><!-- /.Themes -->
683
+ </div><!-- /.theme-browser -->
684
+ <?php
685
+ }
686
+ }
inc/class-demo-content.php DELETED
@@ -1,716 +0,0 @@
1
- <?php
2
- class FT_Demo_Content {
3
-
4
- public $dir;
5
- public $url;
6
-
7
- public $processed_posts;
8
-
9
-
10
- /**
11
- * XML file to import
12
- * Export by WP Exporter
13
- * @var string file path
14
- */
15
- public $xml_file;
16
-
17
-
18
- public $config_file;
19
-
20
- /**
21
- * Option key to store data in $option_file
22
- *
23
- * Data type: JSON string
24
- *
25
- * @see update_option
26
- * @var string option_key
27
- */
28
- public $option_key;
29
-
30
- public $config_data;
31
-
32
- function __construct( $options = array() ){
33
-
34
- $options = wp_parse_args( $options, array(
35
- 'option_key' => '',
36
- 'dummy-data' => '', // file ext
37
- 'config' => ''
38
- ));
39
-
40
- $this->xml_file = $options['dummy-data'];
41
- $this->config_file = $options['config'];
42
- $this->option_key = $options['option_key'];
43
-
44
- }
45
-
46
-
47
-
48
- /**
49
- * Import options
50
- *
51
- * Export by function get_options
52
- *
53
- * @see get_option
54
- * @param $option_key
55
- * @param $file
56
- * @return bool
57
- */
58
- function import_options( $option_key, $file ) {
59
- if ( ! file_exists( $file ) ) {
60
- return false;
61
- }
62
- if ( ! $option_key ) {
63
- return false;
64
- }
65
- $data = file_get_contents( $file );
66
- $data = json_decode( $data , true );
67
-
68
- update_option( $option_key, $data );
69
- }
70
-
71
- /**
72
- * Import widget JSON data
73
- *
74
- * @since 0.4
75
- * @global array $wp_registered_sidebars
76
- * @param object $data JSON widget data from .wie file
77
- * @return array Results array
78
- */
79
- function import_widgets( $data ) {
80
-
81
- global $wp_registered_sidebars;
82
-
83
- // Have valid data?
84
- // If no data or could not decode
85
- if ( empty( $data ) ) {
86
- return false;
87
- }
88
-
89
- // Get all available widgets site supports
90
- $available_widgets = $this->get_available_widgets();
91
-
92
- // Get all existing widget instances
93
- $widget_instances = array();
94
- foreach ( $available_widgets as $widget_data ) {
95
- $widget_instances[$widget_data['id_base']] = get_option( 'widget_' . $widget_data['id_base'] );
96
- }
97
-
98
- // Begin results
99
- $results = array();
100
-
101
- // Loop import data's sidebars
102
- foreach ( $data as $sidebar_id => $widgets ) {
103
-
104
- // Skip inactive widgets
105
- // (should not be in export file)
106
- if ( 'wp_inactive_widgets' == $sidebar_id ) {
107
- continue;
108
- }
109
-
110
- // Check if sidebar is available on this site
111
- // Otherwise add widgets to inactive, and say so
112
- if ( isset( $wp_registered_sidebars[$sidebar_id] ) ) {
113
- $sidebar_available = true;
114
- $use_sidebar_id = $sidebar_id;
115
- $sidebar_message_type = 'success';
116
- $sidebar_message = '';
117
- } else {
118
- $sidebar_available = false;
119
- $use_sidebar_id = 'wp_inactive_widgets'; // add to inactive if sidebar does not exist in theme
120
- $sidebar_message_type = 'error';
121
- $sidebar_message = __( 'Sidebar does not exist in theme (using Inactive)', 'onepress-import' );
122
- }
123
-
124
- // Result for sidebar
125
- $results[$sidebar_id]['name'] = ! empty( $wp_registered_sidebars[$sidebar_id]['name'] ) ? $wp_registered_sidebars[$sidebar_id]['name'] : $sidebar_id; // sidebar name if theme supports it; otherwise ID
126
- $results[$sidebar_id]['message_type'] = $sidebar_message_type;
127
- $results[$sidebar_id]['message'] = $sidebar_message;
128
- $results[$sidebar_id]['widgets'] = array();
129
-
130
- // Loop widgets
131
- foreach ( $widgets as $widget_instance_id => $widget ) {
132
-
133
- $fail = false;
134
-
135
- // Get id_base (remove -# from end) and instance ID number
136
- $id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id );
137
- $instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id );
138
-
139
- // Does site support this widget?
140
- if ( ! $fail && ! isset( $available_widgets[$id_base] ) ) {
141
- $fail = true;
142
- $widget_message_type = 'error';
143
- $widget_message = __( 'Site does not support widget', 'onepress-import' ); // explain why widget not imported
144
- }
145
-
146
- // Filter to modify settings object before conversion to array and import
147
- // Leave this filter here for backwards compatibility with manipulating objects (before conversion to array below)
148
- // Ideally the newer wie_widget_settings_array below will be used instead of this
149
- $widget = apply_filters( 'wie_widget_settings', $widget ); // object
150
-
151
- // Convert multidimensional objects to multidimensional arrays
152
- // Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays
153
- // Without this, they are imported as objects and cause fatal error on Widgets page
154
- // If this creates problems for plugins that do actually intend settings in objects then may need to consider other approach: https://wordpress.org/support/topic/problem-with-array-of-arrays
155
- // It is probably much more likely that arrays are used than objects, however
156
- $widget = json_decode( json_encode( $widget ), true );
157
-
158
- // Filter to modify settings array
159
- // This is preferred over the older wie_widget_settings filter above
160
- // Do before identical check because changes may make it identical to end result (such as URL replacements)
161
- $widget = apply_filters( 'wie_widget_settings_array', $widget );
162
-
163
- // Does widget with identical settings already exist in same sidebar?
164
- if ( ! $fail && isset( $widget_instances[$id_base] ) ) {
165
-
166
- // Get existing widgets in this sidebar
167
- $sidebars_widgets = get_option( 'sidebars_widgets' );
168
- $sidebar_widgets = isset( $sidebars_widgets[$use_sidebar_id] ) ? $sidebars_widgets[$use_sidebar_id] : array(); // check Inactive if that's where will go
169
-
170
- // Loop widgets with ID base
171
- $single_widget_instances = ! empty( $widget_instances[$id_base] ) ? $widget_instances[$id_base] : array();
172
- foreach ( $single_widget_instances as $check_id => $check_widget ) {
173
-
174
- // Is widget in same sidebar and has identical settings?
175
- if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) {
176
-
177
- $fail = true;
178
- $widget_message_type = 'warning';
179
- $widget_message = __( 'Widget already exists', 'onepress-import' ); // explain why widget not imported
180
-
181
- break;
182
-
183
- }
184
-
185
- }
186
-
187
- }
188
-
189
- // No failure
190
- if ( ! $fail ) {
191
-
192
- // Add widget instance
193
- $single_widget_instances = get_option( 'widget_' . $id_base ); // all instances for that widget ID base, get fresh every time
194
- $single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array( '_multiwidget' => 1 ); // start fresh if have to
195
- $single_widget_instances[] = $widget; // add it
196
-
197
- // Get the key it was given
198
- end( $single_widget_instances );
199
- $new_instance_id_number = key( $single_widget_instances );
200
-
201
- // If key is 0, make it 1
202
- // When 0, an issue can occur where adding a widget causes data from other widget to load, and the widget doesn't stick (reload wipes it)
203
- if ( '0' === strval( $new_instance_id_number ) ) {
204
- $new_instance_id_number = 1;
205
- $single_widget_instances[$new_instance_id_number] = $single_widget_instances[0];
206
- unset( $single_widget_instances[0] );
207
- }
208
-
209
- // Move _multiwidget to end of array for uniformity
210
- if ( isset( $single_widget_instances['_multiwidget'] ) ) {
211
- $multiwidget = $single_widget_instances['_multiwidget'];
212
- unset( $single_widget_instances['_multiwidget'] );
213
- $single_widget_instances['_multiwidget'] = $multiwidget;
214
- }
215
-
216
- // Update option with new widget
217
- update_option( 'widget_' . $id_base, $single_widget_instances );
218
-
219
- // Assign widget instance to sidebar
220
- $sidebars_widgets = get_option( 'sidebars_widgets' ); // which sidebars have which widgets, get fresh every time
221
- $new_instance_id = $id_base . '-' . $new_instance_id_number; // use ID number from new widget instance
222
- $sidebars_widgets[$use_sidebar_id][] = $new_instance_id; // add new instance to sidebar
223
- update_option( 'sidebars_widgets', $sidebars_widgets ); // save the amended data
224
-
225
- // After widget import action
226
- $after_widget_import = array(
227
- 'sidebar' => $use_sidebar_id,
228
- 'sidebar_old' => $sidebar_id,
229
- 'widget' => $widget,
230
- 'widget_type' => $id_base,
231
- 'widget_id' => $new_instance_id,
232
- 'widget_id_old' => $widget_instance_id,
233
- 'widget_id_num' => $new_instance_id_number,
234
- 'widget_id_num_old' => $instance_id_number
235
- );
236
- do_action( 'wie_after_widget_import', $after_widget_import );
237
-
238
- // Success message
239
- if ( $sidebar_available ) {
240
- $widget_message_type = 'success';
241
- $widget_message = __( 'Imported', 'onepress-import' );
242
- } else {
243
- $widget_message_type = 'warning';
244
- $widget_message = __( 'Imported to Inactive', 'onepress-import' );
245
- }
246
-
247
- }
248
-
249
- // Result for widget instance
250
- $results[$sidebar_id]['widgets'][$widget_instance_id]['name'] = isset( $available_widgets[$id_base]['name'] ) ? $available_widgets[$id_base]['name'] : $id_base; // widget name or ID if name not available (not supported by site)
251
- $results[$sidebar_id]['widgets'][$widget_instance_id]['title'] = ! empty( $widget['title'] ) ? $widget['title'] : __( 'No Title', 'onepress-import' ); // show "No Title" if widget instance is untitled
252
- $results[$sidebar_id]['widgets'][$widget_instance_id]['message_type'] = $widget_message_type;
253
- $results[$sidebar_id]['widgets'][$widget_instance_id]['message'] = $widget_message;
254
-
255
- }
256
-
257
- }
258
-
259
- // Return results
260
- return $results;
261
-
262
- }
263
-
264
- /**
265
- * JSON customize data
266
- *
267
- * Export by function get_theme_mods
268
- *
269
- * @see get_theme_mods
270
- *
271
- * @param $file
272
- * @return bool
273
- */
274
- function import_customize( $data ) {
275
- if ( ! is_array( $data ) ) {
276
- return false;
277
- }
278
- // Loop through the mods.
279
- foreach ( $data as $key => $val ) {
280
- // Save the mod.
281
- set_theme_mod( $key, $val );
282
- }
283
-
284
- }
285
-
286
- /**
287
- * Import XML file
288
- *
289
- * xml file export by WP exporter
290
- *
291
- * @param $file
292
- */
293
- function import_xml( $file ){
294
-
295
- if ( ! is_file( $file ) ) {
296
- die( 'xml_not_found' );
297
- }
298
-
299
- if ( ! defined('WP_LOAD_IMPORTERS') ) {
300
- define( 'WP_LOAD_IMPORTERS', true );
301
- }
302
-
303
- require_once ABSPATH . 'wp-admin/includes/import.php';
304
- $importer_error = false;
305
-
306
- if ( !class_exists( 'WP_Importer' ) ) {
307
- $class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
308
- if ( file_exists( $class_wp_importer ) ){
309
- require_once($class_wp_importer);
310
- } else {
311
- $importer_error = true;
312
- }
313
- }
314
-
315
- if ( !class_exists( 'WP_Import' ) ) {
316
- $class_wp_import = dirname( __FILE__ ) .'/wordpress-importer.php';
317
- if ( file_exists( $class_wp_import ) ) {
318
- require_once $class_wp_import;
319
- } else {
320
- $importer_error = true;
321
- }
322
- }
323
-
324
- if( $importer_error ){
325
- esc_html_e( "Error load importer.", 'ftdi');
326
- } else {
327
- $wp_import = new WP_Import();
328
- $wp_import->fetch_attachments = true; // download attachment
329
- $wp_import->import( $file );
330
- $this->processed_posts = $wp_import->processed_posts;
331
- do_action( 'ft_import_after_xml_imported', $wp_import );
332
- return $wp_import;
333
- }
334
- return false;
335
- }
336
-
337
- /**
338
- * Available widgets
339
- *
340
- * Gather site's widgets into array with ID base, name, etc.
341
- * Used by export and import functions.
342
- *
343
- * @since 0.4
344
- * @global array $wp_registered_widget_updates
345
- * @return array Widget information
346
- */
347
- static function get_available_widgets() {
348
-
349
- global $wp_registered_widget_controls;
350
-
351
- $widget_controls = $wp_registered_widget_controls;
352
-
353
- $available_widgets = array();
354
-
355
- foreach ( $widget_controls as $widget ) {
356
-
357
- if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[$widget['id_base']] ) ) { // no dupes
358
-
359
- $available_widgets[$widget['id_base']]['id_base'] = $widget['id_base'];
360
- $available_widgets[$widget['id_base']]['name'] = $widget['name'];
361
-
362
- }
363
-
364
- }
365
-
366
- return $available_widgets;
367
-
368
- }
369
-
370
-
371
- /**
372
- * Generate Widgets export data
373
- *
374
- * @since 0.1
375
- * @return string Export file contents
376
- */
377
- static function generate_widgets_export_data() {
378
-
379
- // Get all available widgets site supports
380
- $available_widgets = self::get_available_widgets();
381
-
382
- // Get all widget instances for each widget
383
- $widget_instances = array();
384
- foreach ( $available_widgets as $widget_data ) {
385
-
386
- // Get all instances for this ID base
387
- $instances = get_option( 'widget_' . $widget_data['id_base'] );
388
-
389
- // Have instances
390
- if ( ! empty( $instances ) ) {
391
-
392
- // Loop instances
393
- foreach ( $instances as $instance_id => $instance_data ) {
394
-
395
- // Key is ID (not _multiwidget)
396
- if ( is_numeric( $instance_id ) ) {
397
- $unique_instance_id = $widget_data['id_base'] . '-' . $instance_id;
398
- $widget_instances[$unique_instance_id] = $instance_data;
399
- }
400
-
401
- }
402
-
403
- }
404
-
405
- }
406
-
407
- // Gather sidebars with their widget instances
408
- $sidebars_widgets = get_option( 'sidebars_widgets' ); // get sidebars and their unique widgets IDs
409
- $sidebars_widget_instances = array();
410
- foreach ( $sidebars_widgets as $sidebar_id => $widget_ids ) {
411
-
412
- // Skip inactive widgets
413
- if ( 'wp_inactive_widgets' == $sidebar_id ) {
414
- continue;
415
- }
416
-
417
- // Skip if no data or not an array (array_version)
418
- if ( ! is_array( $widget_ids ) || empty( $widget_ids ) ) {
419
- continue;
420
- }
421
-
422
- // Loop widget IDs for this sidebar
423
- foreach ( $widget_ids as $widget_id ) {
424
-
425
- // Is there an instance for this widget ID?
426
- if ( isset( $widget_instances[$widget_id] ) ) {
427
-
428
- // Add to array
429
- $sidebars_widget_instances[$sidebar_id][$widget_id] = $widget_instances[$widget_id];
430
-
431
- }
432
-
433
- }
434
-
435
- }
436
-
437
- // Filter pre-encoded data
438
- $data = apply_filters( 'ft_demo_export_widgets_data', $sidebars_widget_instances );
439
-
440
- // Encode the data for file contents
441
- return $data;
442
-
443
- }
444
-
445
- /**
446
- * generate theme customize data
447
- *
448
- * @return mixed|string|void
449
- */
450
- static function generate_theme_mods_export_data(){
451
- $data = get_theme_mods();
452
- if ( ! $data ) {
453
- return '';
454
- }
455
- return json_encode( $data ) ;
456
- }
457
-
458
- /**
459
- * generate option data
460
- *
461
- * @param $option_key
462
- * @return mixed|string|void
463
- */
464
- static function generate_options_export_data( $option_key ){
465
- $options = get_option( $option_key , array() );
466
- if ( ! $options ) {
467
- return '';
468
- }
469
- $options = stripslashes_deep( $options );
470
- return json_encode( $options ) ;
471
- }
472
-
473
- static function get_update_keys(){
474
-
475
- $key = 'ft_demo_customizer_keys';
476
- $theme_slug = get_option( 'stylesheet' );
477
- $data = get_option( $key );
478
- if ( ! is_array( $data ) ) {
479
- $data = array();
480
- }
481
- if ( isset( $data[ $theme_slug ] ) ){
482
- return $data[ $theme_slug ];
483
- }
484
-
485
- $r = wp_remote_post( admin_url( 'customize.php' ), array(
486
- 'method' => 'POST',
487
- 'timeout' => 45,
488
- 'redirection' => 5,
489
- 'httpversion' => '1.0',
490
- 'blocking' => true,
491
- 'headers' => array(),
492
- 'cookies' => array(
493
- SECURE_AUTH_COOKIE => $_COOKIE[ SECURE_AUTH_COOKIE ],
494
- AUTH_COOKIE => $_COOKIE[ AUTH_COOKIE ],
495
- LOGGED_IN_COOKIE => $_COOKIE[ LOGGED_IN_COOKIE ],
496
- )
497
- )
498
- );
499
-
500
- if ( is_wp_error( $r ) ) {
501
- return false;
502
- } else {
503
- global $wpdb;
504
-
505
- $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $key ) );
506
- $notoptions = wp_cache_get( 'notoptions', 'options' );
507
- // Has to be get_row instead of get_var because of funkiness with 0, false, null values
508
- if ( is_object( $row ) ) {
509
- $value = $row->option_value;
510
- $data = apply_filters( 'option_' . $key, maybe_unserialize( $value ), $key );
511
- wp_cache_add( $key, $value, 'options' );
512
- } else { // option does not exist, so we must cache its non-existence
513
- if ( ! is_array( $notoptions ) ) {
514
- $notoptions = array();
515
- }
516
- $notoptions[$key] = true;
517
- wp_cache_set( 'notoptions', $notoptions, 'options' );
518
-
519
- /** This filter is documented in wp-includes/option.php */
520
- $data = apply_filters( 'default_option_' . $key, '', $key );
521
- }
522
-
523
- if ( ! is_array( $data ) ) {
524
- $data = array();
525
- }
526
-
527
- if ( isset( $data[ $theme_slug ] ) ) {
528
- return $data[ $theme_slug ];
529
- }
530
- }
531
-
532
- return false;
533
- }
534
-
535
-
536
- static function generate_config(){
537
- $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
538
- // Just update the customizer keys
539
-
540
- $regen_keys = self::get_update_keys();
541
-
542
- $config = array(
543
- 'home_url' => home_url('/'),
544
- 'menus' => $nav_menu_locations,
545
- 'pages' => array(
546
- 'page_on_front' => get_option( 'page_on_front' ),
547
- 'page_for_posts' => get_option( 'page_for_posts' ),
548
- ),
549
- 'options' => array(
550
- 'show_on_front' => get_option( 'show_on_front' )
551
- ),
552
- 'theme_mods' => get_theme_mods(),
553
- 'widgets' => self::generate_widgets_export_data(),
554
- 'customizer_keys' => $regen_keys
555
- );
556
-
557
- $config = apply_filters( 'ft_demo_generate_config', $config );
558
-
559
- return json_encode( $config );
560
- }
561
-
562
- /**
563
- * Run import
564
- */
565
- function import(){
566
- $wp_import = $this->import_xml( $this->xml_file );
567
-
568
- $this->config_data = file_get_contents( $this->config_file );
569
- $this->config_data = json_decode( $this->config_data , true );
570
- $this->config_data = wp_parse_args( $this->config_data, array(
571
- 'home_url' => '',
572
- 'menus' => array(),
573
- 'pages' => array(),
574
- 'options' => array(),
575
- 'theme_mods' => array(),
576
- 'widgets' => array(),
577
- 'customizer_keys' => array()
578
- ) );
579
- $this->import_customize( $this->config_data['theme_mods'] );
580
- $this->import_widgets( $this->config_data['widgets'] );
581
-
582
- if ( $wp_import ) {
583
-
584
- // Update options
585
- foreach ( ( array ) $this->config_data['options'] as $k => $v ) {
586
- update_option( $k, $v );
587
- }
588
-
589
- // Update general pages.
590
- if ( is_array( $this->config_data['pages'] ) ) {
591
- foreach ( $this->config_data['pages'] as $k => $post_id ) {
592
- if ( $post_id ) {
593
- $id = isset( $wp_import->processed_posts[ $post_id ] ) ? $wp_import->processed_posts[ $post_id ] : $post_id;
594
- update_option( $k, $id );
595
- }
596
- }
597
- }
598
-
599
- // Update menus
600
- $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
601
- foreach ( ( array ) $this->config_data['menus'] as $k => $menu_id ) {
602
- if ( $menu_id ) {
603
- $id = isset( $wp_import->processed_terms[ $menu_id ] ) ? $wp_import->processed_terms[ $menu_id ] : $menu_id;
604
- $nav_menu_locations[ $k ] = $id;
605
- }
606
- }
607
- set_theme_mod( 'nav_menu_locations', $nav_menu_locations );
608
-
609
- // Update menu links
610
- if ( $this->config_data['home_url'] ) {
611
-
612
- $demo_url = trailingslashit( $this->config_data['home_url'] );
613
- $home_url = site_url('/');
614
-
615
- global $wpdb;
616
-
617
- $sql = $wpdb->prepare(
618
- "UPDATE {$wpdb->postmeta} SET `meta_value` = REPLACE (`meta_value`, '%s', '%s')",
619
- $demo_url,
620
- $home_url
621
- );
622
-
623
- $wpdb->query($sql);
624
-
625
- }
626
-
627
- // Re-setup meta keys
628
- foreach ( ( array ) $this->config_data['customizer_keys'] as $k => $replace_keys ) {
629
- $this->resetup_repeater_page_ids( $k , $replace_keys, $wp_import->processed_posts, $this->config_data['home_url'] );
630
- }
631
-
632
- echo 'demo_imported';
633
- } else {
634
- echo 'demo_import_failed';
635
- }
636
-
637
- do_action( 'ft_import_after_imported', $wp_import );
638
- }
639
-
640
-
641
- function resetup_repeater_page_ids( $theme_mod_name, $list_keys, $processed_posts = array(), $url ='', $option_type = 'theme_mod' ){
642
- // Setup service
643
- $data = get_theme_mod( $theme_mod_name );
644
- if ( is_string( $list_keys ) ) {
645
- switch( $list_keys ) {
646
- case 'media':
647
- $new_data = $processed_posts[ $data ];
648
- if ( $option_type == 'option' ) {
649
- update_option( $theme_mod_name , $new_data );
650
- } else {
651
- set_theme_mod( $theme_mod_name , $new_data );
652
- }
653
- break;
654
- }
655
- return;
656
- }
657
-
658
- if ( is_string( $data ) ) {
659
- $data = json_decode( $data, true );
660
- }
661
- if ( ! is_array( $data ) ) {
662
- return false;
663
- }
664
- if ( ! is_array( $processed_posts ) ) {
665
- return false;
666
- }
667
-
668
- if ( $url ) {
669
- $url = trailingslashit( $this->config_data['home_url'] );
670
- }
671
-
672
- $home = home_url('/');
673
-
674
-
675
- foreach ($list_keys as $key_info) {
676
- if ($key_info['type'] == 'post' || $key_info['type'] == 'page') {
677
- foreach ($data as $k => $item) {
678
- if (isset($item[$key_info['key']]) && isset ($processed_posts[$item[$key_info['key']]])) {
679
- $data[$k][$key_info['key']] = $processed_posts[$item[$key_info['key']]];
680
- }
681
- }
682
- } elseif ($key_info['type'] == 'media') {
683
-
684
- $main_key = $key_info['key'];
685
- $sub_key_id = 'id';
686
- $sub_key_url = 'url';
687
- if ($main_key) {
688
-
689
- foreach ($data as $k => $item) {
690
- if (isset($item[$sub_key_id]) && is_array($item[$sub_key_id])) {
691
- if (isset ($item[$main_key][$sub_key_id])) {
692
- $data[$item][$main_key][$sub_key_id] = $processed_posts[$item[$main_key][$sub_key_id]];
693
- }
694
- if (isset ($item[$main_key][$sub_key_url])) {
695
- $data[$item][$main_key][$sub_key_url] = str_replace($url, $home, $item[$main_key][$sub_key_url]);
696
- }
697
- }
698
- }
699
-
700
- }
701
-
702
-
703
- }
704
- }
705
-
706
-
707
- if ( $option_type == 'option' ) {
708
- update_option( $theme_mod_name , $data );
709
- } else {
710
- set_theme_mod( $theme_mod_name , $data );
711
- }
712
-
713
-
714
- }
715
-
716
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/class-progress.php ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by PhpStorm.
4
+ * User: truongsa
5
+ * Date: 9/16/17
6
+ * Time: 9:10 AM
7
+ */
8
+
9
+
10
+ class Demo_Contents_Progress {
11
+
12
+
13
+ private $config_data= array();
14
+ private $tgmpa;
15
+
16
+ function __construct()
17
+ {
18
+ add_action( 'wp_ajax_demo_contents__import', array( $this, 'ajax_import' ) );
19
+ add_action( 'admin_enqueue_scripts', array( $this, 'checking_plugins' ), 900, 1 );
20
+ }
21
+
22
+ function checking_plugins( $hook ) {
23
+
24
+ if( $hook != 'themes.php' ) {
25
+ return;
26
+ }
27
+ if ( ! isset( $_REQUEST['__checking_plugins'] ) ) {
28
+ return;
29
+ }
30
+
31
+ $plugins = Demo_Contents::get_instance()->dashboard->get_tgmpa_plugins();
32
+ ob_clean();
33
+ ob_flush();
34
+
35
+ ob_start();
36
+ wp_send_json_success( $plugins );
37
+ die();
38
+ }
39
+
40
+
41
+ /**
42
+ * @see https://github.com/devinsays/edd-theme-updater/blob/master/updater/theme-updater.php
43
+ */
44
+ function ajax_import(){
45
+
46
+ if ( ! class_exists( 'Merlin_WXR_Parser' ) ) {
47
+ require DEMO_CONTENT_PATH. 'inc/merlin-wp/includes/class-merlin-xml-parser.php' ;
48
+ }
49
+
50
+ if ( ! class_exists( 'Merlin_Importer' ) ) {
51
+ require DEMO_CONTENT_PATH .'inc/merlin-wp/includes/class-merlin-importer.php';
52
+ }
53
+
54
+ if ( ! current_user_can( 'import' ) ) {
55
+ wp_send_json_error( __( "You have not permissions to import.", 'demo-contents' ) );
56
+ }
57
+
58
+ $doing = isset( $_REQUEST['doing'] ) ? sanitize_text_field( $_REQUEST['doing'] ) : '';
59
+ if ( ! $doing ) {
60
+ wp_send_json_error( __( "No actions to do", 'demo-contents' ) );
61
+ }
62
+
63
+ // Current theme for import
64
+ $current_theme = isset( $_REQUEST['current_theme'] ) ? $_REQUEST['current_theme'] : false;
65
+
66
+ $current_theme = wp_parse_args( $current_theme, array(
67
+ 'name' => '',
68
+ 'slug' => '',
69
+ 'demo_version' => '',
70
+ 'demo_name' => '',
71
+ 'activate' => '',
72
+ 'xml_id' => '',
73
+ 'json_id' => ''
74
+ ) );
75
+ $current_theme_slug = false;
76
+ $current_theme_demo_version = false;
77
+ if ( ! $current_theme || ! is_array( $current_theme ) || ! isset( $current_theme['slug'] ) || ! $current_theme['slug'] ) {
78
+ wp_send_json_error( __( 'Not theme selected', 'demo-contents' ) );
79
+ }
80
+
81
+ $current_theme_slug = sanitize_text_field( $current_theme['slug'] );
82
+ if ( $current_theme['demo_version'] ) {
83
+ $current_theme_demo_version = sanitize_text_field( $current_theme['demo_version'] );
84
+ }
85
+
86
+ $themes = Demo_Contents::get_instance()->dashboard->setup_themes();
87
+ // if is_activate theme
88
+ if ( $doing == 'activate_theme' ) {
89
+ switch_theme( $current_theme_slug );
90
+ wp_send_json_success( array( 'msg' => sprintf( __( '%s theme activated', 'demo-contents' ), $themes[ $current_theme_slug ]['name'] ) ) );
91
+ }
92
+
93
+ if ( $doing == 'checking_resources' ){
94
+ $file_data = $this->maybe_remote_download_data_files( $current_theme );
95
+ if ( ! $file_data || empty( $file_data ) ) {
96
+ wp_send_json_error( sprintf( __( 'Demo data not found for <strong>%s</strong>. However you can import demo content by upload your demo files below.', 'demo-contents' ) , $themes[ $current_theme_slug ]['name'] ) );
97
+ } else {
98
+ wp_send_json_success( __( 'Demo data ready for import.', 'demo-contents' ) );
99
+ }
100
+ }
101
+
102
+ /// Check theme activate
103
+ if ( ! isset( $themes[ $current_theme_slug ] ) ) {
104
+ wp_send_json_error( __( 'This theme have not installed.', 'demo-contents' ) );
105
+ }
106
+
107
+
108
+ //wp_send_json_success(); // just for test
109
+ $file_data = $this->maybe_remote_download_data_files( $current_theme );
110
+ if ( ! $file_data || empty( $file_data ) ) {
111
+ wp_send_json_error( array( 'type' => 'no-files', 'msg' => __( 'Dummy data files not found', 'demo-contents' ), 'files' => $file_data ) );
112
+ }
113
+
114
+ $transient_key = '_demo_content_'.$current_theme_slug;
115
+ if ( $current_theme_demo_version ) {
116
+ $transient_key .= '-demos-'.$current_theme_demo_version;
117
+ }
118
+
119
+
120
+ $importer = new Merlin_Importer();
121
+ $content = get_transient( $transient_key );
122
+ if ( ! $content ) {
123
+ $parser = new Merlin_WXR_Parser();
124
+ $content = $parser->parse( $file_data['xml'] );
125
+ set_transient( $transient_key, $content, DAY_IN_SECONDS );
126
+ }
127
+
128
+ if ( is_wp_error( $content ) ) {
129
+ wp_send_json_error( __( 'Dummy content empty', 'demo-contents' ) );
130
+ }
131
+
132
+ // Setup config
133
+ $option_config = get_transient( $transient_key.'-json' );
134
+ if ( false === $option_config ) {
135
+ if ( file_exists( $file_data['xml'] ) ) {
136
+ global $wp_filesystem;
137
+ WP_Filesystem();
138
+ $file_contents = $wp_filesystem->get_contents( $file_data['json'] );
139
+ $option_config = json_decode( $file_contents, true );
140
+ set_transient( $transient_key.'-json', $option_config, DAY_IN_SECONDS ) ;
141
+ }
142
+ }
143
+
144
+ switch ( $doing ) {
145
+ case 'import_users':
146
+ if ( ! empty( $content['users'] ) ) {
147
+ $importer->import_users( $content['users'] );
148
+ }
149
+ break;
150
+
151
+ case 'import_categories':
152
+ if ( ! empty( $content['categories'] ) ) {
153
+ $importer->importTerms( $content['categories'] );
154
+ }
155
+ break;
156
+ case 'import_tags':
157
+ if ( ! empty( $content['tags'] ) ) {
158
+ $importer->importTerms( $content['tags'] );
159
+ }
160
+ break;
161
+ case 'import_taxs':
162
+ if ( ! empty( $content['terms'] ) ) {
163
+ $importer->importTerms( $content['terms'] );
164
+ }
165
+ break;
166
+ case 'import_posts':
167
+ if ( ! empty( $content['posts'] ) ) {
168
+ $importer->importPosts( $content['posts'] );
169
+ }
170
+ $importer->remapImportedData();
171
+ do_action( 'demo_contents_import_posts_completed', $this, $importer );
172
+
173
+ break;
174
+
175
+ case 'import_theme_options':
176
+ if ( isset( $option_config['options'] ) ){
177
+ $this->importOptions( $option_config['options'] );
178
+ }
179
+
180
+ // Setup Pages
181
+ $processed_posts = get_transient('_wxr_imported_posts') ? : array();
182
+ if ( isset( $option_config['pages'] ) ){
183
+ foreach ( $option_config['pages'] as $key => $id ) {
184
+ $val = isset( $processed_posts[ $id ] ) ? $processed_posts[ $id ] : null ;
185
+ update_option( $key, $val );
186
+ }
187
+ }
188
+
189
+ do_action( 'demo_contents_import_theme_options_completed', $this, $importer );
190
+
191
+ break;
192
+
193
+ case 'import_widgets':
194
+ $this->config_data = $option_config;
195
+ if ( isset( $option_config['widgets'] ) ){
196
+ // print_r( $option_config['widgets'] );
197
+ if ( ! isset( $this->config_data['widgets_config'] ) ) {
198
+ $this->config_data['widgets_config'] = array();
199
+ }
200
+ $importer->importWidgets( $option_config['widgets'], $this->config_data['widgets_config'] );
201
+ do_action( 'demo_contents_import_widgets_completed', $this, $importer );
202
+ }
203
+ break;
204
+
205
+ case 'import_customize':
206
+ if ( isset( $option_config['theme_mods'] ) ){
207
+ $importer->importThemeOptions( $option_config['theme_mods'] );
208
+ if ( isset( $option_config['customizer_keys'] ) ) {
209
+ foreach ( ( array ) $option_config['customizer_keys'] as $k => $list_key ) {
210
+ $this->resetup_repeater_page_ids( $k, $list_key );
211
+ }
212
+ }
213
+ }
214
+
215
+ do_action( 'demo_contents_import_customize_completed', $this, $importer );
216
+
217
+ // Remove transient data if is live mod
218
+ if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
219
+ $importer->importEnd();
220
+ // Delete file
221
+ $file_key = '_demo_contents_file_'.$current_theme_slug;
222
+ if ( $current_theme_demo_version ) {
223
+ $file_key .= '-demos-'.$current_theme_demo_version;
224
+ }
225
+ $files = get_transient( $file_key );
226
+ if ( is_array( $files ) ) {
227
+ foreach ( $files as $file_id ) {
228
+ wp_delete_attachment( $file_id );
229
+ }
230
+ }
231
+ delete_transient( $transient_key );
232
+ delete_transient( $transient_key.'-json' );
233
+ }
234
+
235
+ break;
236
+
237
+ } // end switch action
238
+
239
+ wp_send_json_success( );
240
+
241
+ }
242
+
243
+
244
+ function importOptions( $options ){
245
+ if ( empty( $options ) ) {
246
+ return ;
247
+ }
248
+ foreach ( $options as $option_name => $ops ) {
249
+ update_option( $option_name, $ops );
250
+ }
251
+ }
252
+
253
+
254
+ function resetup_repeater_page_ids( $theme_mod_name = null, $list_keys, $url ='', $option_type = 'theme_mod' ){
255
+
256
+ $processed_posts = get_transient('_wxr_imported_posts') ? : array();
257
+ if ( ! is_array( $processed_posts ) ) {
258
+ $processed_posts = array();
259
+ }
260
+
261
+ // Setup service
262
+ $data = get_theme_mod( $theme_mod_name );
263
+ if ( is_string( $list_keys ) ) {
264
+ switch( $list_keys ) {
265
+ case 'media':
266
+ $new_data = $processed_posts[ $data ];
267
+ if ( $option_type == 'option' ) {
268
+ update_option( $theme_mod_name , $new_data );
269
+ } else {
270
+ set_theme_mod( $theme_mod_name , $new_data );
271
+ }
272
+ break;
273
+ }
274
+ return;
275
+ }
276
+
277
+ if ( is_string( $data ) ) {
278
+ $data = json_decode( $data, true );
279
+ }
280
+ if ( ! is_array( $data ) ) {
281
+ return false;
282
+ }
283
+ if ( ! is_array( $processed_posts ) ) {
284
+ return false;
285
+ }
286
+
287
+ if ( $url ) {
288
+ $url = trailingslashit( $this->config_data['home_url'] );
289
+ }
290
+
291
+ $home = home_url('/');
292
+
293
+
294
+ foreach ($list_keys as $key_info) {
295
+ if ($key_info['type'] == 'post' || $key_info['type'] == 'page') {
296
+ foreach ($data as $k => $item) {
297
+ if (isset($item[$key_info['key']]) && isset ($processed_posts[$item[$key_info['key']]])) {
298
+ $data[$k][$key_info['key']] = $processed_posts[ $item[$key_info['key']] ];
299
+ }
300
+ }
301
+ } elseif ($key_info['type'] == 'media') {
302
+
303
+ $main_key = $key_info['key'];
304
+ $sub_key_id = 'id';
305
+ $sub_key_url = 'url';
306
+ if ($main_key) {
307
+
308
+ foreach ($data as $k => $item) {
309
+ if ( isset ($item[$main_key]) && is_array($item[$main_key])) {
310
+ if (isset ($item[$main_key][$sub_key_id]) ) {
311
+ $data[$k][$main_key][$sub_key_id] = $processed_posts[ $item[$main_key] [$sub_key_id] ];
312
+ }
313
+ if (isset ($item[$main_key][$sub_key_url])) {
314
+ $data[$k][$main_key][$sub_key_url] = str_replace($url, $home, $item[$main_key][$sub_key_url]);
315
+ }
316
+ }
317
+ }
318
+
319
+ }
320
+ }
321
+ }
322
+
323
+ if ( $option_type == 'option' ) {
324
+ update_option( $theme_mod_name , $data );
325
+ } else {
326
+ set_theme_mod( $theme_mod_name , $data );
327
+ }
328
+
329
+ }
330
+
331
+
332
+ function maybe_remote_download_data_files( $args = array() ) {
333
+ $args = wp_parse_args( $args, array(
334
+ 'slug' => '',
335
+ 'demo_version' => '',
336
+ 'xml_id' => '',
337
+ 'json_id' => ''
338
+ ) );
339
+
340
+ $theme_name = $args['slug'];
341
+ $demo_version = $args['demo_version'];
342
+ if ( $args['xml_id'] ) {
343
+ return array( 'xml' => get_attached_file( $args['xml_id'] ), 'json' => get_attached_file( $args['json_id'] ) );
344
+ }
345
+
346
+ if ( ! $theme_name ) {
347
+ return false;
348
+ }
349
+
350
+ $sub_path = $theme_name;
351
+ if ( $demo_version ) {
352
+ $sub_path .= '/demos/'.$demo_version;
353
+ }
354
+ $prefix_name = str_replace( '/', '-', $sub_path );
355
+
356
+ $xml_file_name = $prefix_name .'-dummy-data.xml' ;
357
+ $config_file_name = $prefix_name .'-config.json';
358
+
359
+ $xml_file = false;
360
+ $config_file = false;
361
+
362
+ $files_data = get_transient( '_demo_contents_file_'.$prefix_name );
363
+
364
+ // If have cache
365
+ if ( ! empty( $files_data ) ) {
366
+ $files_data = wp_parse_args( $files_data, array( 'xml' => '', 'json' => '' ) );
367
+ $xml_file = get_attached_file( $files_data['xml'] );
368
+ $config_file = get_attached_file( $files_data['json'] );
369
+ if ( ! empty( $xml_file ) ) {
370
+ return array( 'xml' => $xml_file, 'json' => $config_file );
371
+ }
372
+ }
373
+
374
+ $remote_folder = apply_filters( 'demo_contents_remote_demo_data_folder_url', false );
375
+
376
+ if ( ! $remote_folder ) {
377
+ $repo = Demo_Contents::get_github_repo();
378
+ $remote_folder = 'https://raw.githubusercontent.com/'.$repo.'/master/';
379
+ }
380
+ $remote_folder = trailingslashit( $remote_folder );
381
+
382
+
383
+ $xml_id = Demo_Contents::download_file( $remote_folder.$sub_path.'/dummy-data.xml', $xml_file_name );
384
+ if ( $xml_id ) {
385
+ $xml_file = get_attached_file( $xml_id );
386
+ }
387
+
388
+ $config_id = Demo_Contents::download_file( $remote_folder.$sub_path.'/config.json', $config_file_name );
389
+ if ( $config_id ) {
390
+ $config_file = get_attached_file( $config_id );
391
+ }
392
+
393
+ if ( ! empty( $xml_file ) ) {
394
+ set_transient( '_demo_contents_file_'.$prefix_name, array( 'xml' => $xml_id, 'json' => $config_id ) );
395
+ return array( 'xml' => $xml_file, 'json' => $config_file );
396
+ }
397
+
398
+ return false;
399
+
400
+ }
401
+
402
+ }
inc/class-tgm-plugin-activation.php ADDED
@@ -0,0 +1,3853 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin installation and activation for WordPress themes.
4
+ *
5
+ * Please note that this is a drop-in library for a theme or plugin.
6
+ * The authors of this library (Thomas, Gary and Juliette) are NOT responsible
7
+ * for the support of your plugin or theme. Please contact the plugin
8
+ * or theme author for support.
9
+ *
10
+ * @package TGM-Plugin-Activation
11
+ * @version 2.6.1
12
+ * @link http://tgmpluginactivation.com/
13
+ * @author Thomas Griffin, Gary Jones, Juliette Reinders Folmer
14
+ * @copyright Copyright (c) 2011, Thomas Griffin
15
+ * @license GPL-2.0+
16
+ */
17
+
18
+ /*
19
+ Copyright 2011 Thomas Griffin (thomasgriffinmedia.com)
20
+
21
+ This program is free software; you can redistribute it and/or modify
22
+ it under the terms of the GNU General Public License, version 2, as
23
+ published by the Free Software Foundation.
24
+
25
+ This program is distributed in the hope that it will be useful,
26
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
27
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
+ GNU General Public License for more details.
29
+
30
+ You should have received a copy of the GNU General Public License
31
+ along with this program; if not, write to the Free Software
32
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33
+ */
34
+
35
+ if ( ! class_exists( 'TGM_Plugin_Activation' ) ) {
36
+
37
+ /**
38
+ * Automatic plugin installation and activation library.
39
+ *
40
+ * Creates a way to automatically install and activate plugins from within themes.
41
+ * The plugins can be either bundled, downloaded from the WordPress
42
+ * Plugin Repository or downloaded from another external source.
43
+ *
44
+ * @since 1.0.0
45
+ *
46
+ * @package TGM-Plugin-Activation
47
+ * @author Thomas Griffin
48
+ * @author Gary Jones
49
+ */
50
+ class TGM_Plugin_Activation {
51
+ /**
52
+ * TGMPA version number.
53
+ *
54
+ * @since 2.5.0
55
+ *
56
+ * @const string Version number.
57
+ */
58
+ const TGMPA_VERSION = '2.6.1';
59
+
60
+ /**
61
+ * Regular expression to test if a URL is a WP plugin repo URL.
62
+ *
63
+ * @const string Regex.
64
+ *
65
+ * @since 2.5.0
66
+ */
67
+ const WP_REPO_REGEX = '|^http[s]?://wordpress\.org/(?:extend/)?plugins/|';
68
+
69
+ /**
70
+ * Arbitrary regular expression to test if a string starts with a URL.
71
+ *
72
+ * @const string Regex.
73
+ *
74
+ * @since 2.5.0
75
+ */
76
+ const IS_URL_REGEX = '|^http[s]?://|';
77
+
78
+ /**
79
+ * Holds a copy of itself, so it can be referenced by the class name.
80
+ *
81
+ * @since 1.0.0
82
+ *
83
+ * @var TGM_Plugin_Activation
84
+ */
85
+ public static $instance;
86
+
87
+ /**
88
+ * Holds arrays of plugin details.
89
+ *
90
+ * @since 1.0.0
91
+ * @since 2.5.0 the array has the plugin slug as an associative key.
92
+ *
93
+ * @var array
94
+ */
95
+ public $plugins = array();
96
+
97
+ /**
98
+ * Holds arrays of plugin names to use to sort the plugins array.
99
+ *
100
+ * @since 2.5.0
101
+ *
102
+ * @var array
103
+ */
104
+ protected $sort_order = array();
105
+
106
+ /**
107
+ * Whether any plugins have the 'force_activation' setting set to true.
108
+ *
109
+ * @since 2.5.0
110
+ *
111
+ * @var bool
112
+ */
113
+ protected $has_forced_activation = false;
114
+
115
+ /**
116
+ * Whether any plugins have the 'force_deactivation' setting set to true.
117
+ *
118
+ * @since 2.5.0
119
+ *
120
+ * @var bool
121
+ */
122
+ protected $has_forced_deactivation = false;
123
+
124
+ /**
125
+ * Name of the unique ID to hash notices.
126
+ *
127
+ * @since 2.4.0
128
+ *
129
+ * @var string
130
+ */
131
+ public $id = 'tgmpa';
132
+
133
+ /**
134
+ * Name of the query-string argument for the admin page.
135
+ *
136
+ * @since 1.0.0
137
+ *
138
+ * @var string
139
+ */
140
+ protected $menu = 'tgmpa-install-plugins';
141
+
142
+ /**
143
+ * Parent menu file slug.
144
+ *
145
+ * @since 2.5.0
146
+ *
147
+ * @var string
148
+ */
149
+ public $parent_slug = 'themes.php';
150
+
151
+ /**
152
+ * Capability needed to view the plugin installation menu item.
153
+ *
154
+ * @since 2.5.0
155
+ *
156
+ * @var string
157
+ */
158
+ public $capability = 'edit_theme_options';
159
+
160
+ /**
161
+ * Default absolute path to folder containing bundled plugin zip files.
162
+ *
163
+ * @since 2.0.0
164
+ *
165
+ * @var string Absolute path prefix to zip file location for bundled plugins. Default is empty string.
166
+ */
167
+ public $default_path = '';
168
+
169
+ /**
170
+ * Flag to show admin notices or not.
171
+ *
172
+ * @since 2.1.0
173
+ *
174
+ * @var boolean
175
+ */
176
+ public $has_notices = true;
177
+
178
+ /**
179
+ * Flag to determine if the user can dismiss the notice nag.
180
+ *
181
+ * @since 2.4.0
182
+ *
183
+ * @var boolean
184
+ */
185
+ public $dismissable = true;
186
+
187
+ /**
188
+ * Message to be output above nag notice if dismissable is false.
189
+ *
190
+ * @since 2.4.0
191
+ *
192
+ * @var string
193
+ */
194
+ public $dismiss_msg = '';
195
+
196
+ /**
197
+ * Flag to set automatic activation of plugins. Off by default.
198
+ *
199
+ * @since 2.2.0
200
+ *
201
+ * @var boolean
202
+ */
203
+ public $is_automatic = false;
204
+
205
+ /**
206
+ * Optional message to display before the plugins table.
207
+ *
208
+ * @since 2.2.0
209
+ *
210
+ * @var string Message filtered by wp_kses_post(). Default is empty string.
211
+ */
212
+ public $message = '';
213
+
214
+ /**
215
+ * Holds configurable array of strings.
216
+ *
217
+ * Default values are added in the constructor.
218
+ *
219
+ * @since 2.0.0
220
+ *
221
+ * @var array
222
+ */
223
+ public $strings = array();
224
+
225
+ /**
226
+ * Holds the version of WordPress.
227
+ *
228
+ * @since 2.4.0
229
+ *
230
+ * @var int
231
+ */
232
+ public $wp_version;
233
+
234
+ /**
235
+ * Holds the hook name for the admin page.
236
+ *
237
+ * @since 2.5.0
238
+ *
239
+ * @var string
240
+ */
241
+ public $page_hook;
242
+
243
+ /**
244
+ * Adds a reference of this object to $instance, populates default strings,
245
+ * does the tgmpa_init action hook, and hooks in the interactions to init.
246
+ *
247
+ * {@internal This method should be `protected`, but as too many TGMPA implementations
248
+ * haven't upgraded beyond v2.3.6 yet, this gives backward compatibility issues.
249
+ * Reverted back to public for the time being.}}
250
+ *
251
+ * @since 1.0.0
252
+ *
253
+ * @see TGM_Plugin_Activation::init()
254
+ */
255
+ public function __construct() {
256
+ // Set the current WordPress version.
257
+ $this->wp_version = $GLOBALS['wp_version'];
258
+
259
+ // Announce that the class is ready, and pass the object (for advanced use).
260
+ do_action_ref_array( 'tgmpa_init', array( $this ) );
261
+
262
+ /*
263
+ * Load our text domain and allow for overloading the fall-back file.
264
+ *
265
+ * {@internal IMPORTANT! If this code changes, review the regex in the custom TGMPA
266
+ * generator on the website.}}
267
+ */
268
+ add_action( 'init', array( $this, 'load_textdomain' ), 5 );
269
+ add_filter( 'load_textdomain_mofile', array( $this, 'overload_textdomain_mofile' ), 10, 2 );
270
+
271
+ // When the rest of WP has loaded, kick-start the rest of the class.
272
+ add_action( 'init', array( $this, 'init' ) );
273
+ }
274
+
275
+ /**
276
+ * Magic method to (not) set protected properties from outside of this class.
277
+ *
278
+ * {@internal hackedihack... There is a serious bug in v2.3.2 - 2.3.6 where the `menu` property
279
+ * is being assigned rather than tested in a conditional, effectively rendering it useless.
280
+ * This 'hack' prevents this from happening.}}
281
+ *
282
+ * @see https://github.com/TGMPA/TGM-Plugin-Activation/blob/2.3.6/tgm-plugin-activation/class-tgm-plugin-activation.php#L1593
283
+ *
284
+ * @since 2.5.2
285
+ *
286
+ * @param string $name Name of an inaccessible property.
287
+ * @param mixed $value Value to assign to the property.
288
+ * @return void Silently fail to set the property when this is tried from outside of this class context.
289
+ * (Inside this class context, the __set() method if not used as there is direct access.)
290
+ */
291
+ public function __set( $name, $value ) {
292
+ return;
293
+ }
294
+
295
+ /**
296
+ * Magic method to get the value of a protected property outside of this class context.
297
+ *
298
+ * @since 2.5.2
299
+ *
300
+ * @param string $name Name of an inaccessible property.
301
+ * @return mixed The property value.
302
+ */
303
+ public function __get( $name ) {
304
+ return $this->{$name};
305
+ }
306
+
307
+ /**
308
+ * Initialise the interactions between this class and WordPress.
309
+ *
310
+ * Hooks in three new methods for the class: admin_menu, notices and styles.
311
+ *
312
+ * @since 2.0.0
313
+ *
314
+ * @see TGM_Plugin_Activation::admin_menu()
315
+ * @see TGM_Plugin_Activation::notices()
316
+ * @see TGM_Plugin_Activation::styles()
317
+ */
318
+ public function init() {
319
+ /**
320
+ * By default TGMPA only loads on the WP back-end and not in an Ajax call. Using this filter
321
+ * you can overrule that behaviour.
322
+ *
323
+ * @since 2.5.0
324
+ *
325
+ * @param bool $load Whether or not TGMPA should load.
326
+ * Defaults to the return of `is_admin() && ! defined( 'DOING_AJAX' )`.
327
+ */
328
+ if ( true !== apply_filters( 'tgmpa_load', ( is_admin() && ! defined( 'DOING_AJAX' ) ) ) ) {
329
+ return;
330
+ }
331
+
332
+ // Load class strings.
333
+ $this->strings = array(
334
+ 'page_title' => __( 'Install Required Plugins', 'tgmpa' ),
335
+ 'menu_title' => __( 'Install Plugins', 'tgmpa' ),
336
+ /* translators: %s: plugin name. */
337
+ 'installing' => __( 'Installing Plugin: %s', 'tgmpa' ),
338
+ /* translators: %s: plugin name. */
339
+ 'updating' => __( 'Updating Plugin: %s', 'tgmpa' ),
340
+ 'oops' => __( 'Something went wrong with the plugin API.', 'tgmpa' ),
341
+ 'notice_can_install_required' => _n_noop(
342
+ /* translators: 1: plugin name(s). */
343
+ 'This theme requires the following plugin: %1$s.',
344
+ 'This theme requires the following plugins: %1$s.',
345
+ 'tgmpa'
346
+ ),
347
+ 'notice_can_install_recommended' => _n_noop(
348
+ /* translators: 1: plugin name(s). */
349
+ 'This theme recommends the following plugin: %1$s.',
350
+ 'This theme recommends the following plugins: %1$s.',
351
+ 'tgmpa'
352
+ ),
353
+ 'notice_ask_to_update' => _n_noop(
354
+ /* translators: 1: plugin name(s). */
355
+ 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.',
356
+ 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.',
357
+ 'tgmpa'
358
+ ),
359
+ 'notice_ask_to_update_maybe' => _n_noop(
360
+ /* translators: 1: plugin name(s). */
361
+ 'There is an update available for: %1$s.',
362
+ 'There are updates available for the following plugins: %1$s.',
363
+ 'tgmpa'
364
+ ),
365
+ 'notice_can_activate_required' => _n_noop(
366
+ /* translators: 1: plugin name(s). */
367
+ 'The following required plugin is currently inactive: %1$s.',
368
+ 'The following required plugins are currently inactive: %1$s.',
369
+ 'tgmpa'
370
+ ),
371
+ 'notice_can_activate_recommended' => _n_noop(
372
+ /* translators: 1: plugin name(s). */
373
+ 'The following recommended plugin is currently inactive: %1$s.',
374
+ 'The following recommended plugins are currently inactive: %1$s.',
375
+ 'tgmpa'
376
+ ),
377
+ 'install_link' => _n_noop(
378
+ 'Begin installing plugin',
379
+ 'Begin installing plugins',
380
+ 'tgmpa'
381
+ ),
382
+ 'update_link' => _n_noop(
383
+ 'Begin updating plugin',
384
+ 'Begin updating plugins',
385
+ 'tgmpa'
386
+ ),
387
+ 'activate_link' => _n_noop(
388
+ 'Begin activating plugin',
389
+ 'Begin activating plugins',
390
+ 'tgmpa'
391
+ ),
392
+ 'return' => __( 'Return to Required Plugins Installer', 'tgmpa' ),
393
+ 'dashboard' => __( 'Return to the Dashboard', 'tgmpa' ),
394
+ 'plugin_activated' => __( 'Plugin activated successfully.', 'tgmpa' ),
395
+ 'activated_successfully' => __( 'The following plugin was activated successfully:', 'tgmpa' ),
396
+ /* translators: 1: plugin name. */
397
+ 'plugin_already_active' => __( 'No action taken. Plugin %1$s was already active.', 'tgmpa' ),
398
+ /* translators: 1: plugin name. */
399
+ 'plugin_needs_higher_version' => __( 'Plugin not activated. A higher version of %s is needed for this theme. Please update the plugin.', 'tgmpa' ),
400
+ /* translators: 1: dashboard link. */
401
+ 'complete' => __( 'All plugins installed and activated successfully. %1$s', 'tgmpa' ),
402
+ 'dismiss' => __( 'Dismiss this notice', 'tgmpa' ),
403
+ 'notice_cannot_install_activate' => __( 'There are one or more required or recommended plugins to install, update or activate.', 'tgmpa' ),
404
+ 'contact_admin' => __( 'Please contact the administrator of this site for help.', 'tgmpa' ),
405
+ );
406
+
407
+ do_action( 'tgmpa_register' );
408
+
409
+ /* After this point, the plugins should be registered and the configuration set. */
410
+
411
+ // Proceed only if we have plugins to handle.
412
+ if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
413
+ return;
414
+ }
415
+
416
+ // Set up the menu and notices if we still have outstanding actions.
417
+ if ( true !== $this->is_tgmpa_complete() ) {
418
+ // Sort the plugins.
419
+ array_multisort( $this->sort_order, SORT_ASC, $this->plugins );
420
+
421
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
422
+ add_action( 'admin_head', array( $this, 'dismiss' ) );
423
+
424
+ // Prevent the normal links from showing underneath a single install/update page.
425
+ add_filter( 'install_plugin_complete_actions', array( $this, 'actions' ) );
426
+ add_filter( 'update_plugin_complete_actions', array( $this, 'actions' ) );
427
+
428
+ if ( $this->has_notices ) {
429
+ add_action( 'admin_notices', array( $this, 'notices' ) );
430
+ add_action( 'admin_init', array( $this, 'admin_init' ), 1 );
431
+ add_action( 'admin_enqueue_scripts', array( $this, 'thickbox' ) );
432
+ }
433
+ }
434
+
435
+ // If needed, filter plugin action links.
436
+ add_action( 'load-plugins.php', array( $this, 'add_plugin_action_link_filters' ), 1 );
437
+
438
+ // Make sure things get reset on switch theme.
439
+ add_action( 'switch_theme', array( $this, 'flush_plugins_cache' ) );
440
+
441
+ if ( $this->has_notices ) {
442
+ add_action( 'switch_theme', array( $this, 'update_dismiss' ) );
443
+ }
444
+
445
+ // Setup the force activation hook.
446
+ if ( true === $this->has_forced_activation ) {
447
+ add_action( 'admin_init', array( $this, 'force_activation' ) );
448
+ }
449
+
450
+ // Setup the force deactivation hook.
451
+ if ( true === $this->has_forced_deactivation ) {
452
+ add_action( 'switch_theme', array( $this, 'force_deactivation' ) );
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Load translations.
458
+ *
459
+ * @since 2.6.0
460
+ *
461
+ * (@internal Uses `load_theme_textdomain()` rather than `load_plugin_textdomain()` to
462
+ * get round the different ways of handling the path and deprecated notices being thrown
463
+ * and such. For plugins, the actual file name will be corrected by a filter.}}
464
+ *
465
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
466
+ * generator on the website.}}
467
+ */
468
+ public function load_textdomain() {
469
+ if ( is_textdomain_loaded( 'tgmpa' ) ) {
470
+ return;
471
+ }
472
+
473
+ if ( false !== strpos( __FILE__, WP_PLUGIN_DIR ) || false !== strpos( __FILE__, WPMU_PLUGIN_DIR ) ) {
474
+ // Plugin, we'll need to adjust the file name.
475
+ add_action( 'load_textdomain_mofile', array( $this, 'correct_plugin_mofile' ), 10, 2 );
476
+ load_theme_textdomain( 'tgmpa', dirname( __FILE__ ) . '/languages' );
477
+ remove_action( 'load_textdomain_mofile', array( $this, 'correct_plugin_mofile' ), 10 );
478
+ } else {
479
+ load_theme_textdomain( 'tgmpa', dirname( __FILE__ ) . '/languages' );
480
+ }
481
+ }
482
+
483
+ /**
484
+ * Correct the .mo file name for (must-use) plugins.
485
+ *
486
+ * Themese use `/path/{locale}.mo` while plugins use `/path/{text-domain}-{locale}.mo`.
487
+ *
488
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
489
+ * generator on the website.}}
490
+ *
491
+ * @since 2.6.0
492
+ *
493
+ * @param string $mofile Full path to the target mofile.
494
+ * @param string $domain The domain for which a language file is being loaded.
495
+ * @return string $mofile
496
+ */
497
+ public function correct_plugin_mofile( $mofile, $domain ) {
498
+ // Exit early if not our domain (just in case).
499
+ if ( 'tgmpa' !== $domain ) {
500
+ return $mofile;
501
+ }
502
+ return preg_replace( '`/([a-z]{2}_[A-Z]{2}.mo)$`', '/tgmpa-$1', $mofile );
503
+ }
504
+
505
+ /**
506
+ * Potentially overload the fall-back translation file for the current language.
507
+ *
508
+ * WP, by default since WP 3.7, will load a local translation first and if none
509
+ * can be found, will try and find a translation in the /wp-content/languages/ directory.
510
+ * As this library is theme/plugin agnostic, translation files for TGMPA can exist both
511
+ * in the WP_LANG_DIR /plugins/ subdirectory as well as in the /themes/ subdirectory.
512
+ *
513
+ * This method makes sure both directories are checked.
514
+ *
515
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
516
+ * generator on the website.}}
517
+ *
518
+ * @since 2.6.0
519
+ *
520
+ * @param string $mofile Full path to the target mofile.
521
+ * @param string $domain The domain for which a language file is being loaded.
522
+ * @return string $mofile
523
+ */
524
+ public function overload_textdomain_mofile( $mofile, $domain ) {
525
+ // Exit early if not our domain, not a WP_LANG_DIR load or if the file exists and is readable.
526
+ if ( 'tgmpa' !== $domain || false === strpos( $mofile, WP_LANG_DIR ) || @is_readable( $mofile ) ) {
527
+ return $mofile;
528
+ }
529
+
530
+ // Current fallback file is not valid, let's try the alternative option.
531
+ if ( false !== strpos( $mofile, '/themes/' ) ) {
532
+ return str_replace( '/themes/', '/plugins/', $mofile );
533
+ } elseif ( false !== strpos( $mofile, '/plugins/' ) ) {
534
+ return str_replace( '/plugins/', '/themes/', $mofile );
535
+ } else {
536
+ return $mofile;
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Hook in plugin action link filters for the WP native plugins page.
542
+ *
543
+ * - Prevent activation of plugins which don't meet the minimum version requirements.
544
+ * - Prevent deactivation of force-activated plugins.
545
+ * - Add update notice if update available.
546
+ *
547
+ * @since 2.5.0
548
+ */
549
+ public function add_plugin_action_link_filters() {
550
+ foreach ( $this->plugins as $slug => $plugin ) {
551
+ if ( false === $this->can_plugin_activate( $slug ) ) {
552
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_activate' ), 20 );
553
+ }
554
+
555
+ if ( true === $plugin['force_activation'] ) {
556
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_deactivate' ), 20 );
557
+ }
558
+
559
+ if ( false !== $this->does_plugin_require_update( $slug ) ) {
560
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_update' ), 20 );
561
+ }
562
+ }
563
+ }
564
+
565
+ /**
566
+ * Remove the 'Activate' link on the WP native plugins page if the plugin does not meet the
567
+ * minimum version requirements.
568
+ *
569
+ * @since 2.5.0
570
+ *
571
+ * @param array $actions Action links.
572
+ * @return array
573
+ */
574
+ public function filter_plugin_action_links_activate( $actions ) {
575
+ unset( $actions['activate'] );
576
+
577
+ return $actions;
578
+ }
579
+
580
+ /**
581
+ * Remove the 'Deactivate' link on the WP native plugins page if the plugin has been set to force activate.
582
+ *
583
+ * @since 2.5.0
584
+ *
585
+ * @param array $actions Action links.
586
+ * @return array
587
+ */
588
+ public function filter_plugin_action_links_deactivate( $actions ) {
589
+ unset( $actions['deactivate'] );
590
+
591
+ return $actions;
592
+ }
593
+
594
+ /**
595
+ * Add a 'Requires update' link on the WP native plugins page if the plugin does not meet the
596
+ * minimum version requirements.
597
+ *
598
+ * @since 2.5.0
599
+ *
600
+ * @param array $actions Action links.
601
+ * @return array
602
+ */
603
+ public function filter_plugin_action_links_update( $actions ) {
604
+ $actions['update'] = sprintf(
605
+ '<a href="%1$s" title="%2$s" class="edit">%3$s</a>',
606
+ esc_url( $this->get_tgmpa_status_url( 'update' ) ),
607
+ esc_attr__( 'This plugin needs to be updated to be compatible with your theme.', 'tgmpa' ),
608
+ esc_html__( 'Update Required', 'tgmpa' )
609
+ );
610
+
611
+ return $actions;
612
+ }
613
+
614
+ /**
615
+ * Handles calls to show plugin information via links in the notices.
616
+ *
617
+ * We get the links in the admin notices to point to the TGMPA page, rather
618
+ * than the typical plugin-install.php file, so we can prepare everything
619
+ * beforehand.
620
+ *
621
+ * WP does not make it easy to show the plugin information in the thickbox -
622
+ * here we have to require a file that includes a function that does the
623
+ * main work of displaying it, enqueue some styles, set up some globals and
624
+ * finally call that function before exiting.
625
+ *
626
+ * Down right easy once you know how...
627
+ *
628
+ * Returns early if not the TGMPA page.
629
+ *
630
+ * @since 2.1.0
631
+ *
632
+ * @global string $tab Used as iframe div class names, helps with styling
633
+ * @global string $body_id Used as the iframe body ID, helps with styling
634
+ *
635
+ * @return null Returns early if not the TGMPA page.
636
+ */
637
+ public function admin_init() {
638
+ if ( ! $this->is_tgmpa_page() ) {
639
+ return;
640
+ }
641
+
642
+ if ( isset( $_REQUEST['tab'] ) && 'plugin-information' === $_REQUEST['tab'] ) {
643
+ // Needed for install_plugin_information().
644
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
645
+
646
+ wp_enqueue_style( 'plugin-install' );
647
+
648
+ global $tab, $body_id;
649
+ $body_id = 'plugin-information';
650
+ // @codingStandardsIgnoreStart
651
+ $tab = 'plugin-information';
652
+ // @codingStandardsIgnoreEnd
653
+
654
+ install_plugin_information();
655
+
656
+ exit;
657
+ }
658
+ }
659
+
660
+ /**
661
+ * Enqueue thickbox scripts/styles for plugin info.
662
+ *
663
+ * Thickbox is not automatically included on all admin pages, so we must
664
+ * manually enqueue it for those pages.
665
+ *
666
+ * Thickbox is only loaded if the user has not dismissed the admin
667
+ * notice or if there are any plugins left to install and activate.
668
+ *
669
+ * @since 2.1.0
670
+ */
671
+ public function thickbox() {
672
+ if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) ) {
673
+ add_thickbox();
674
+ }
675
+ }
676
+
677
+ /**
678
+ * Adds submenu page if there are plugin actions to take.
679
+ *
680
+ * This method adds the submenu page letting users know that a required
681
+ * plugin needs to be installed.
682
+ *
683
+ * This page disappears once the plugin has been installed and activated.
684
+ *
685
+ * @since 1.0.0
686
+ *
687
+ * @see TGM_Plugin_Activation::init()
688
+ * @see TGM_Plugin_Activation::install_plugins_page()
689
+ *
690
+ * @return null Return early if user lacks capability to install a plugin.
691
+ */
692
+ public function admin_menu() {
693
+ // Make sure privileges are correct to see the page.
694
+ if ( ! current_user_can( 'install_plugins' ) ) {
695
+ return;
696
+ }
697
+
698
+ $args = apply_filters(
699
+ 'tgmpa_admin_menu_args',
700
+ array(
701
+ 'parent_slug' => $this->parent_slug, // Parent Menu slug.
702
+ 'page_title' => $this->strings['page_title'], // Page title.
703
+ 'menu_title' => $this->strings['menu_title'], // Menu title.
704
+ 'capability' => $this->capability, // Capability.
705
+ 'menu_slug' => $this->menu, // Menu slug.
706
+ 'function' => array( $this, 'install_plugins_page' ), // Callback.
707
+ )
708
+ );
709
+
710
+ $this->add_admin_menu( $args );
711
+ }
712
+
713
+ /**
714
+ * Add the menu item.
715
+ *
716
+ * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA
717
+ * generator on the website.}}
718
+ *
719
+ * @since 2.5.0
720
+ *
721
+ * @param array $args Menu item configuration.
722
+ */
723
+ protected function add_admin_menu( array $args ) {
724
+ if ( has_filter( 'tgmpa_admin_menu_use_add_theme_page' ) ) {
725
+ _deprecated_function( 'The "tgmpa_admin_menu_use_add_theme_page" filter', '2.5.0', esc_html__( 'Set the parent_slug config variable instead.', 'tgmpa' ) );
726
+ }
727
+
728
+ if ( 'themes.php' === $this->parent_slug ) {
729
+ $this->page_hook = call_user_func( 'add_theme_page', $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
730
+ } else {
731
+ $this->page_hook = call_user_func( 'add_submenu_page', $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
732
+ }
733
+ }
734
+
735
+ /**
736
+ * Echoes plugin installation form.
737
+ *
738
+ * This method is the callback for the admin_menu method function.
739
+ * This displays the admin page and form area where the user can select to install and activate the plugin.
740
+ * Aborts early if we're processing a plugin installation action.
741
+ *
742
+ * @since 1.0.0
743
+ *
744
+ * @return null Aborts early if we're processing a plugin installation action.
745
+ */
746
+ public function install_plugins_page() {
747
+ // Store new instance of plugin table in object.
748
+ $plugin_table = new TGMPA_List_Table;
749
+
750
+ // Return early if processing a plugin installation action.
751
+ if ( ( ( 'tgmpa-bulk-install' === $plugin_table->current_action() || 'tgmpa-bulk-update' === $plugin_table->current_action() ) && $plugin_table->process_bulk_actions() ) || $this->do_plugin_install() ) {
752
+ return;
753
+ }
754
+
755
+ // Force refresh of available plugin information so we'll know about manual updates/deletes.
756
+ wp_clean_plugins_cache( false );
757
+
758
+ ?>
759
+ <div class="tgmpa wrap">
760
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
761
+ <?php $plugin_table->prepare_items(); ?>
762
+
763
+ <?php
764
+ if ( ! empty( $this->message ) && is_string( $this->message ) ) {
765
+ echo wp_kses_post( $this->message );
766
+ }
767
+ ?>
768
+ <?php $plugin_table->views(); ?>
769
+
770
+ <form id="tgmpa-plugins" action="" method="post">
771
+ <input type="hidden" name="tgmpa-page" value="<?php echo esc_attr( $this->menu ); ?>" />
772
+ <input type="hidden" name="plugin_status" value="<?php echo esc_attr( $plugin_table->view_context ); ?>" />
773
+ <?php $plugin_table->display(); ?>
774
+ </form>
775
+ </div>
776
+ <?php
777
+ }
778
+
779
+ /**
780
+ * Installs, updates or activates a plugin depending on the action link clicked by the user.
781
+ *
782
+ * Checks the $_GET variable to see which actions have been
783
+ * passed and responds with the appropriate method.
784
+ *
785
+ * Uses WP_Filesystem to process and handle the plugin installation
786
+ * method.
787
+ *
788
+ * @since 1.0.0
789
+ *
790
+ * @uses WP_Filesystem
791
+ * @uses WP_Error
792
+ * @uses WP_Upgrader
793
+ * @uses Plugin_Upgrader
794
+ * @uses Plugin_Installer_Skin
795
+ * @uses Plugin_Upgrader_Skin
796
+ *
797
+ * @return boolean True on success, false on failure.
798
+ */
799
+ protected function do_plugin_install() {
800
+ if ( empty( $_GET['plugin'] ) ) {
801
+ return false;
802
+ }
803
+
804
+ // All plugin information will be stored in an array for processing.
805
+ $slug = $this->sanitize_key( urldecode( $_GET['plugin'] ) );
806
+
807
+ if ( ! isset( $this->plugins[ $slug ] ) ) {
808
+ return false;
809
+ }
810
+
811
+ // Was an install or upgrade action link clicked?
812
+ if ( ( isset( $_GET['tgmpa-install'] ) && 'install-plugin' === $_GET['tgmpa-install'] ) || ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) ) {
813
+
814
+ $install_type = 'install';
815
+ if ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) {
816
+ $install_type = 'update';
817
+ }
818
+
819
+ check_admin_referer( 'tgmpa-' . $install_type, 'tgmpa-nonce' );
820
+
821
+ // Pass necessary information via URL if WP_Filesystem is needed.
822
+ $url = wp_nonce_url(
823
+ add_query_arg(
824
+ array(
825
+ 'plugin' => urlencode( $slug ),
826
+ 'tgmpa-' . $install_type => $install_type . '-plugin',
827
+ ),
828
+ $this->get_tgmpa_url()
829
+ ),
830
+ 'tgmpa-' . $install_type,
831
+ 'tgmpa-nonce'
832
+ );
833
+
834
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
835
+
836
+ if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, array() ) ) ) {
837
+ return true;
838
+ }
839
+
840
+ if ( ! WP_Filesystem( $creds ) ) {
841
+ request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, array() ); // Setup WP_Filesystem.
842
+ return true;
843
+ }
844
+
845
+ /* If we arrive here, we have the filesystem. */
846
+
847
+ // Prep variables for Plugin_Installer_Skin class.
848
+ $extra = array();
849
+ $extra['slug'] = $slug; // Needed for potentially renaming of directory name.
850
+ $source = $this->get_download_url( $slug );
851
+ $api = ( 'repo' === $this->plugins[ $slug ]['source_type'] ) ? $this->get_plugins_api( $slug ) : null;
852
+ $api = ( false !== $api ) ? $api : null;
853
+
854
+ $url = add_query_arg(
855
+ array(
856
+ 'action' => $install_type . '-plugin',
857
+ 'plugin' => urlencode( $slug ),
858
+ ),
859
+ 'update.php'
860
+ );
861
+
862
+ if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
863
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
864
+ }
865
+
866
+ $title = ( 'update' === $install_type ) ? $this->strings['updating'] : $this->strings['installing'];
867
+ $skin_args = array(
868
+ 'type' => ( 'bundled' !== $this->plugins[ $slug ]['source_type'] ) ? 'web' : 'upload',
869
+ 'title' => sprintf( $title, $this->plugins[ $slug ]['name'] ),
870
+ 'url' => esc_url_raw( $url ),
871
+ 'nonce' => $install_type . '-plugin_' . $slug,
872
+ 'plugin' => '',
873
+ 'api' => $api,
874
+ 'extra' => $extra,
875
+ );
876
+ unset( $title );
877
+
878
+ if ( 'update' === $install_type ) {
879
+ $skin_args['plugin'] = $this->plugins[ $slug ]['file_path'];
880
+ $skin = new Plugin_Upgrader_Skin( $skin_args );
881
+ } else {
882
+ $skin = new Plugin_Installer_Skin( $skin_args );
883
+ }
884
+
885
+ // Create a new instance of Plugin_Upgrader.
886
+ $upgrader = new Plugin_Upgrader( $skin );
887
+
888
+ // Perform the action and install the plugin from the $source urldecode().
889
+ add_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1, 3 );
890
+
891
+ if ( 'update' === $install_type ) {
892
+ // Inject our info into the update transient.
893
+ $to_inject = array( $slug => $this->plugins[ $slug ] );
894
+ $to_inject[ $slug ]['source'] = $source;
895
+ $this->inject_update_info( $to_inject );
896
+
897
+ $upgrader->upgrade( $this->plugins[ $slug ]['file_path'] );
898
+ } else {
899
+ $upgrader->install( $source );
900
+ }
901
+
902
+ remove_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1 );
903
+
904
+ // Make sure we have the correct file path now the plugin is installed/updated.
905
+ $this->populate_file_path( $slug );
906
+
907
+ // Only activate plugins if the config option is set to true and the plugin isn't
908
+ // already active (upgrade).
909
+ if ( $this->is_automatic && ! $this->is_plugin_active( $slug ) ) {
910
+ $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method.
911
+ if ( false === $this->activate_single_plugin( $plugin_activate, $slug, true ) ) {
912
+ return true; // Finish execution of the function early as we encountered an error.
913
+ }
914
+ }
915
+
916
+ $this->show_tgmpa_version();
917
+
918
+ // Display message based on if all plugins are now active or not.
919
+ if ( $this->is_tgmpa_complete() ) {
920
+ echo '<p>', sprintf( esc_html( $this->strings['complete'] ), '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>' ), '</p>';
921
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
922
+ } else {
923
+ echo '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
924
+ }
925
+
926
+ return true;
927
+ } elseif ( isset( $this->plugins[ $slug ]['file_path'], $_GET['tgmpa-activate'] ) && 'activate-plugin' === $_GET['tgmpa-activate'] ) {
928
+ // Activate action link was clicked.
929
+ check_admin_referer( 'tgmpa-activate', 'tgmpa-nonce' );
930
+
931
+ if ( false === $this->activate_single_plugin( $this->plugins[ $slug ]['file_path'], $slug ) ) {
932
+ return true; // Finish execution of the function early as we encountered an error.
933
+ }
934
+ }
935
+
936
+ return false;
937
+ }
938
+
939
+ /**
940
+ * Inject information into the 'update_plugins' site transient as WP checks that before running an update.
941
+ *
942
+ * @since 2.5.0
943
+ *
944
+ * @param array $plugins The plugin information for the plugins which are to be updated.
945
+ */
946
+ public function inject_update_info( $plugins ) {
947
+ $repo_updates = get_site_transient( 'update_plugins' );
948
+
949
+ if ( ! is_object( $repo_updates ) ) {
950
+ $repo_updates = new stdClass;
951
+ }
952
+
953
+ foreach ( $plugins as $slug => $plugin ) {
954
+ $file_path = $plugin['file_path'];
955
+
956
+ if ( empty( $repo_updates->response[ $file_path ] ) ) {
957
+ $repo_updates->response[ $file_path ] = new stdClass;
958
+ }
959
+
960
+ // We only really need to set package, but let's do all we can in case WP changes something.
961
+ $repo_updates->response[ $file_path ]->slug = $slug;
962
+ $repo_updates->response[ $file_path ]->plugin = $file_path;
963
+ $repo_updates->response[ $file_path ]->new_version = $plugin['version'];
964
+ $repo_updates->response[ $file_path ]->package = $plugin['source'];
965
+ if ( empty( $repo_updates->response[ $file_path ]->url ) && ! empty( $plugin['external_url'] ) ) {
966
+ $repo_updates->response[ $file_path ]->url = $plugin['external_url'];
967
+ }
968
+ }
969
+
970
+ set_site_transient( 'update_plugins', $repo_updates );
971
+ }
972
+
973
+ /**
974
+ * Adjust the plugin directory name if necessary.
975
+ *
976
+ * The final destination directory of a plugin is based on the subdirectory name found in the
977
+ * (un)zipped source. In some cases - most notably GitHub repository plugin downloads -, this
978
+ * subdirectory name is not the same as the expected slug and the plugin will not be recognized
979
+ * as installed. This is fixed by adjusting the temporary unzipped source subdirectory name to
980
+ * the expected plugin slug.
981
+ *
982
+ * @since 2.5.0
983
+ *
984
+ * @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/.
985
+ * @param string $remote_source Path to upgrade/zip-file-name.tmp.
986
+ * @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin.
987
+ * @return string $source
988
+ */
989
+ public function maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
990
+ if ( ! $this->is_tgmpa_page() || ! is_object( $GLOBALS['wp_filesystem'] ) ) {
991
+ return $source;
992
+ }
993
+
994
+ // Check for single file plugins.
995
+ $source_files = array_keys( $GLOBALS['wp_filesystem']->dirlist( $remote_source ) );
996
+ if ( 1 === count( $source_files ) && false === $GLOBALS['wp_filesystem']->is_dir( $source ) ) {
997
+ return $source;
998
+ }
999
+
1000
+ // Multi-file plugin, let's see if the directory is correctly named.
1001
+ $desired_slug = '';
1002
+
1003
+ // Figure out what the slug is supposed to be.
1004
+ if ( false === $upgrader->bulk && ! empty( $upgrader->skin->options['extra']['slug'] ) ) {
1005
+ $desired_slug = $upgrader->skin->options['extra']['slug'];
1006
+ } else {
1007
+ // Bulk installer contains less info, so fall back on the info registered here.
1008
+ foreach ( $this->plugins as $slug => $plugin ) {
1009
+ if ( ! empty( $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) && $plugin['name'] === $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) {
1010
+ $desired_slug = $slug;
1011
+ break;
1012
+ }
1013
+ }
1014
+ unset( $slug, $plugin );
1015
+ }
1016
+
1017
+ if ( ! empty( $desired_slug ) ) {
1018
+ $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
1019
+
1020
+ if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
1021
+ $from_path = untrailingslashit( $source );
1022
+ $to_path = trailingslashit( $remote_source ) . $desired_slug;
1023
+
1024
+ if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
1025
+ return trailingslashit( $to_path );
1026
+ } else {
1027
+ return new WP_Error( 'rename_failed', esc_html__( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
1028
+ }
1029
+ } elseif ( empty( $subdir_name ) ) {
1030
+ return new WP_Error( 'packaged_wrong', esc_html__( 'The remote plugin package consists of more than one file, but the files are not packaged in a folder.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
1031
+ }
1032
+ }
1033
+
1034
+ return $source;
1035
+ }
1036
+
1037
+ /**
1038
+ * Activate a single plugin and send feedback about the result to the screen.
1039
+ *
1040
+ * @since 2.5.0
1041
+ *
1042
+ * @param string $file_path Path within wp-plugins/ to main plugin file.
1043
+ * @param string $slug Plugin slug.
1044
+ * @param bool $automatic Whether this is an automatic activation after an install. Defaults to false.
1045
+ * This determines the styling of the output messages.
1046
+ * @return bool False if an error was encountered, true otherwise.
1047
+ */
1048
+ protected function activate_single_plugin( $file_path, $slug, $automatic = false ) {
1049
+ if ( $this->can_plugin_activate( $slug ) ) {
1050
+ $activate = activate_plugin( $file_path );
1051
+
1052
+ if ( is_wp_error( $activate ) ) {
1053
+ echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>',
1054
+ '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
1055
+
1056
+ return false; // End it here if there is an error with activation.
1057
+ } else {
1058
+ if ( ! $automatic ) {
1059
+ // Make sure message doesn't display again if bulk activation is performed
1060
+ // immediately after a single activation.
1061
+ if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
1062
+ echo '<div id="message" class="updated"><p>', esc_html( $this->strings['activated_successfully'] ), ' <strong>', esc_html( $this->plugins[ $slug ]['name'] ), '.</strong></p></div>';
1063
+ }
1064
+ } else {
1065
+ // Simpler message layout for use on the plugin install page.
1066
+ echo '<p>', esc_html( $this->strings['plugin_activated'] ), '</p>';
1067
+ }
1068
+ }
1069
+ } elseif ( $this->is_plugin_active( $slug ) ) {
1070
+ // No simpler message format provided as this message should never be encountered
1071
+ // on the plugin install page.
1072
+ echo '<div id="message" class="error"><p>',
1073
+ sprintf(
1074
+ esc_html( $this->strings['plugin_already_active'] ),
1075
+ '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
1076
+ ),
1077
+ '</p></div>';
1078
+ } elseif ( $this->does_plugin_require_update( $slug ) ) {
1079
+ if ( ! $automatic ) {
1080
+ // Make sure message doesn't display again if bulk activation is performed
1081
+ // immediately after a single activation.
1082
+ if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
1083
+ echo '<div id="message" class="error"><p>',
1084
+ sprintf(
1085
+ esc_html( $this->strings['plugin_needs_higher_version'] ),
1086
+ '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
1087
+ ),
1088
+ '</p></div>';
1089
+ }
1090
+ } else {
1091
+ // Simpler message layout for use on the plugin install page.
1092
+ echo '<p>', sprintf( esc_html( $this->strings['plugin_needs_higher_version'] ), esc_html( $this->plugins[ $slug ]['name'] ) ), '</p>';
1093
+ }
1094
+ }
1095
+
1096
+ return true;
1097
+ }
1098
+
1099
+ /**
1100
+ * Echoes required plugin notice.
1101
+ *
1102
+ * Outputs a message telling users that a specific plugin is required for
1103
+ * their theme. If appropriate, it includes a link to the form page where
1104
+ * users can install and activate the plugin.
1105
+ *
1106
+ * Returns early if we're on the Install page.
1107
+ *
1108
+ * @since 1.0.0
1109
+ *
1110
+ * @global object $current_screen
1111
+ *
1112
+ * @return null Returns early if we're on the Install page.
1113
+ */
1114
+ public function notices() {
1115
+ // Remove nag on the install page / Return early if the nag message has been dismissed or user < author.
1116
+ if ( ( $this->is_tgmpa_page() || $this->is_core_update_page() ) || get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) || ! current_user_can( apply_filters( 'tgmpa_show_admin_notice_capability', 'publish_posts' ) ) ) {
1117
+ return;
1118
+ }
1119
+
1120
+ // Store for the plugin slugs by message type.
1121
+ $message = array();
1122
+
1123
+ // Initialize counters used to determine plurality of action link texts.
1124
+ $install_link_count = 0;
1125
+ $update_link_count = 0;
1126
+ $activate_link_count = 0;
1127
+ $total_required_action_count = 0;
1128
+
1129
+ foreach ( $this->plugins as $slug => $plugin ) {
1130
+ if ( $this->is_plugin_active( $slug ) && false === $this->does_plugin_have_update( $slug ) ) {
1131
+ continue;
1132
+ }
1133
+
1134
+ if ( ! $this->is_plugin_installed( $slug ) ) {
1135
+ if ( current_user_can( 'install_plugins' ) ) {
1136
+ $install_link_count++;
1137
+
1138
+ if ( true === $plugin['required'] ) {
1139
+ $message['notice_can_install_required'][] = $slug;
1140
+ } else {
1141
+ $message['notice_can_install_recommended'][] = $slug;
1142
+ }
1143
+ }
1144
+ if ( true === $plugin['required'] ) {
1145
+ $total_required_action_count++;
1146
+ }
1147
+ } else {
1148
+ if ( ! $this->is_plugin_active( $slug ) && $this->can_plugin_activate( $slug ) ) {
1149
+ if ( current_user_can( 'activate_plugins' ) ) {
1150
+ $activate_link_count++;
1151
+
1152
+ if ( true === $plugin['required'] ) {
1153
+ $message['notice_can_activate_required'][] = $slug;
1154
+ } else {
1155
+ $message['notice_can_activate_recommended'][] = $slug;
1156
+ }
1157
+ }
1158
+ if ( true === $plugin['required'] ) {
1159
+ $total_required_action_count++;
1160
+ }
1161
+ }
1162
+
1163
+ if ( $this->does_plugin_require_update( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1164
+
1165
+ if ( current_user_can( 'update_plugins' ) ) {
1166
+ $update_link_count++;
1167
+
1168
+ if ( $this->does_plugin_require_update( $slug ) ) {
1169
+ $message['notice_ask_to_update'][] = $slug;
1170
+ } elseif ( false !== $this->does_plugin_have_update( $slug ) ) {
1171
+ $message['notice_ask_to_update_maybe'][] = $slug;
1172
+ }
1173
+ }
1174
+ if ( true === $plugin['required'] ) {
1175
+ $total_required_action_count++;
1176
+ }
1177
+ }
1178
+ }
1179
+ }
1180
+ unset( $slug, $plugin );
1181
+
1182
+ // If we have notices to display, we move forward.
1183
+ if ( ! empty( $message ) || $total_required_action_count > 0 ) {
1184
+ krsort( $message ); // Sort messages.
1185
+ $rendered = '';
1186
+
1187
+ // As add_settings_error() wraps the final message in a <p> and as the final message can't be
1188
+ // filtered, using <p>'s in our html would render invalid html output.
1189
+ $line_template = '<span style="display: block; margin: 0.5em 0.5em 0 0; clear: both;">%s</span>' . "\n";
1190
+
1191
+ if ( ! current_user_can( 'activate_plugins' ) && ! current_user_can( 'install_plugins' ) && ! current_user_can( 'update_plugins' ) ) {
1192
+ $rendered = esc_html( $this->strings['notice_cannot_install_activate'] ) . ' ' . esc_html( $this->strings['contact_admin'] );
1193
+ $rendered .= $this->create_user_action_links_for_notice( 0, 0, 0, $line_template );
1194
+ } else {
1195
+
1196
+ // If dismissable is false and a message is set, output it now.
1197
+ if ( ! $this->dismissable && ! empty( $this->dismiss_msg ) ) {
1198
+ $rendered .= sprintf( $line_template, wp_kses_post( $this->dismiss_msg ) );
1199
+ }
1200
+
1201
+ // Render the individual message lines for the notice.
1202
+ foreach ( $message as $type => $plugin_group ) {
1203
+ $linked_plugins = array();
1204
+
1205
+ // Get the external info link for a plugin if one is available.
1206
+ foreach ( $plugin_group as $plugin_slug ) {
1207
+ $linked_plugins[] = $this->get_info_link( $plugin_slug );
1208
+ }
1209
+ unset( $plugin_slug );
1210
+
1211
+ $count = count( $plugin_group );
1212
+ $linked_plugins = array_map( array( 'TGMPA_Utils', 'wrap_in_em' ), $linked_plugins );
1213
+ $last_plugin = array_pop( $linked_plugins ); // Pop off last name to prep for readability.
1214
+ $imploded = empty( $linked_plugins ) ? $last_plugin : ( implode( ', ', $linked_plugins ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
1215
+
1216
+ $rendered .= sprintf(
1217
+ $line_template,
1218
+ sprintf(
1219
+ translate_nooped_plural( $this->strings[ $type ], $count, 'tgmpa' ),
1220
+ $imploded,
1221
+ $count
1222
+ )
1223
+ );
1224
+
1225
+ }
1226
+ unset( $type, $plugin_group, $linked_plugins, $count, $last_plugin, $imploded );
1227
+
1228
+ $rendered .= $this->create_user_action_links_for_notice( $install_link_count, $update_link_count, $activate_link_count, $line_template );
1229
+ }
1230
+
1231
+ // Register the nag messages and prepare them to be processed.
1232
+ add_settings_error( 'tgmpa', 'tgmpa', $rendered, $this->get_admin_notice_class() );
1233
+ }
1234
+
1235
+ // Admin options pages already output settings_errors, so this is to avoid duplication.
1236
+ if ( 'options-general' !== $GLOBALS['current_screen']->parent_base ) {
1237
+ $this->display_settings_errors();
1238
+ }
1239
+ }
1240
+
1241
+ /**
1242
+ * Generate the user action links for the admin notice.
1243
+ *
1244
+ * @since 2.6.0
1245
+ *
1246
+ * @param int $install_count Number of plugins to install.
1247
+ * @param int $update_count Number of plugins to update.
1248
+ * @param int $activate_count Number of plugins to activate.
1249
+ * @param int $line_template Template for the HTML tag to output a line.
1250
+ * @return string Action links.
1251
+ */
1252
+ protected function create_user_action_links_for_notice( $install_count, $update_count, $activate_count, $line_template ) {
1253
+ // Setup action links.
1254
+ $action_links = array(
1255
+ 'install' => '',
1256
+ 'update' => '',
1257
+ 'activate' => '',
1258
+ 'dismiss' => $this->dismissable ? '<a href="' . esc_url( wp_nonce_url( add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ), 'tgmpa-dismiss-' . get_current_user_id() ) ) . '" class="dismiss-notice" target="_parent">' . esc_html( $this->strings['dismiss'] ) . '</a>' : '',
1259
+ );
1260
+
1261
+ $link_template = '<a href="%2$s">%1$s</a>';
1262
+
1263
+ if ( current_user_can( 'install_plugins' ) ) {
1264
+ if ( $install_count > 0 ) {
1265
+ $action_links['install'] = sprintf(
1266
+ $link_template,
1267
+ translate_nooped_plural( $this->strings['install_link'], $install_count, 'tgmpa' ),
1268
+ esc_url( $this->get_tgmpa_status_url( 'install' ) )
1269
+ );
1270
+ }
1271
+ if ( $update_count > 0 ) {
1272
+ $action_links['update'] = sprintf(
1273
+ $link_template,
1274
+ translate_nooped_plural( $this->strings['update_link'], $update_count, 'tgmpa' ),
1275
+ esc_url( $this->get_tgmpa_status_url( 'update' ) )
1276
+ );
1277
+ }
1278
+ }
1279
+
1280
+ if ( current_user_can( 'activate_plugins' ) && $activate_count > 0 ) {
1281
+ $action_links['activate'] = sprintf(
1282
+ $link_template,
1283
+ translate_nooped_plural( $this->strings['activate_link'], $activate_count, 'tgmpa' ),
1284
+ esc_url( $this->get_tgmpa_status_url( 'activate' ) )
1285
+ );
1286
+ }
1287
+
1288
+ $action_links = apply_filters( 'tgmpa_notice_action_links', $action_links );
1289
+
1290
+ $action_links = array_filter( (array) $action_links ); // Remove any empty array items.
1291
+
1292
+ if ( ! empty( $action_links ) ) {
1293
+ $action_links = sprintf( $line_template, implode( ' | ', $action_links ) );
1294
+ return apply_filters( 'tgmpa_notice_rendered_action_links', $action_links );
1295
+ } else {
1296
+ return '';
1297
+ }
1298
+ }
1299
+
1300
+ /**
1301
+ * Get admin notice class.
1302
+ *
1303
+ * Work around all the changes to the various admin notice classes between WP 4.4 and 3.7
1304
+ * (lowest supported version by TGMPA).
1305
+ *
1306
+ * @since 2.6.0
1307
+ *
1308
+ * @return string
1309
+ */
1310
+ protected function get_admin_notice_class() {
1311
+ if ( ! empty( $this->strings['nag_type'] ) ) {
1312
+ return sanitize_html_class( strtolower( $this->strings['nag_type'] ) );
1313
+ } else {
1314
+ if ( version_compare( $this->wp_version, '4.2', '>=' ) ) {
1315
+ return 'notice-warning';
1316
+ } elseif ( version_compare( $this->wp_version, '4.1', '>=' ) ) {
1317
+ return 'notice';
1318
+ } else {
1319
+ return 'updated';
1320
+ }
1321
+ }
1322
+ }
1323
+
1324
+ /**
1325
+ * Display settings errors and remove those which have been displayed to avoid duplicate messages showing
1326
+ *
1327
+ * @since 2.5.0
1328
+ */
1329
+ protected function display_settings_errors() {
1330
+ global $wp_settings_errors;
1331
+
1332
+ settings_errors( 'tgmpa' );
1333
+
1334
+ foreach ( (array) $wp_settings_errors as $key => $details ) {
1335
+ if ( 'tgmpa' === $details['setting'] ) {
1336
+ unset( $wp_settings_errors[ $key ] );
1337
+ break;
1338
+ }
1339
+ }
1340
+ }
1341
+
1342
+ /**
1343
+ * Register dismissal of admin notices.
1344
+ *
1345
+ * Acts on the dismiss link in the admin nag messages.
1346
+ * If clicked, the admin notice disappears and will no longer be visible to this user.
1347
+ *
1348
+ * @since 2.1.0
1349
+ */
1350
+ public function dismiss() {
1351
+ if ( isset( $_GET['tgmpa-dismiss'] ) && check_admin_referer( 'tgmpa-dismiss-' . get_current_user_id() ) ) {
1352
+ update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, 1 );
1353
+ }
1354
+ }
1355
+
1356
+ /**
1357
+ * Add individual plugin to our collection of plugins.
1358
+ *
1359
+ * If the required keys are not set or the plugin has already
1360
+ * been registered, the plugin is not added.
1361
+ *
1362
+ * @since 2.0.0
1363
+ *
1364
+ * @param array|null $plugin Array of plugin arguments or null if invalid argument.
1365
+ * @return null Return early if incorrect argument.
1366
+ */
1367
+ public function register( $plugin ) {
1368
+ if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) {
1369
+ return;
1370
+ }
1371
+
1372
+ if ( empty( $plugin['slug'] ) || ! is_string( $plugin['slug'] ) || isset( $this->plugins[ $plugin['slug'] ] ) ) {
1373
+ return;
1374
+ }
1375
+
1376
+ $defaults = array(
1377
+ 'name' => '', // String
1378
+ 'slug' => '', // String
1379
+ 'source' => 'repo', // String
1380
+ 'required' => false, // Boolean
1381
+ 'version' => '', // String
1382
+ 'force_activation' => false, // Boolean
1383
+ 'force_deactivation' => false, // Boolean
1384
+ 'external_url' => '', // String
1385
+ 'is_callable' => '', // String|Array.
1386
+ );
1387
+
1388
+ // Prepare the received data.
1389
+ $plugin = wp_parse_args( $plugin, $defaults );
1390
+
1391
+ // Standardize the received slug.
1392
+ $plugin['slug'] = $this->sanitize_key( $plugin['slug'] );
1393
+
1394
+ // Forgive users for using string versions of booleans or floats for version number.
1395
+ $plugin['version'] = (string) $plugin['version'];
1396
+ $plugin['source'] = empty( $plugin['source'] ) ? 'repo' : $plugin['source'];
1397
+ $plugin['required'] = TGMPA_Utils::validate_bool( $plugin['required'] );
1398
+ $plugin['force_activation'] = TGMPA_Utils::validate_bool( $plugin['force_activation'] );
1399
+ $plugin['force_deactivation'] = TGMPA_Utils::validate_bool( $plugin['force_deactivation'] );
1400
+
1401
+ // Enrich the received data.
1402
+ $plugin['file_path'] = $this->_get_plugin_basename_from_slug( $plugin['slug'] );
1403
+ $plugin['source_type'] = $this->get_plugin_source_type( $plugin['source'] );
1404
+
1405
+ // Set the class properties.
1406
+ $this->plugins[ $plugin['slug'] ] = $plugin;
1407
+ $this->sort_order[ $plugin['slug'] ] = $plugin['name'];
1408
+
1409
+ // Should we add the force activation hook ?
1410
+ if ( true === $plugin['force_activation'] ) {
1411
+ $this->has_forced_activation = true;
1412
+ }
1413
+
1414
+ // Should we add the force deactivation hook ?
1415
+ if ( true === $plugin['force_deactivation'] ) {
1416
+ $this->has_forced_deactivation = true;
1417
+ }
1418
+ }
1419
+
1420
+ /**
1421
+ * Determine what type of source the plugin comes from.
1422
+ *
1423
+ * @since 2.5.0
1424
+ *
1425
+ * @param string $source The source of the plugin as provided, either empty (= WP repo), a file path
1426
+ * (= bundled) or an external URL.
1427
+ * @return string 'repo', 'external', or 'bundled'
1428
+ */
1429
+ protected function get_plugin_source_type( $source ) {
1430
+ if ( 'repo' === $source || preg_match( self::WP_REPO_REGEX, $source ) ) {
1431
+ return 'repo';
1432
+ } elseif ( preg_match( self::IS_URL_REGEX, $source ) ) {
1433
+ return 'external';
1434
+ } else {
1435
+ return 'bundled';
1436
+ }
1437
+ }
1438
+
1439
+ /**
1440
+ * Sanitizes a string key.
1441
+ *
1442
+ * Near duplicate of WP Core `sanitize_key()`. The difference is that uppercase characters *are*
1443
+ * allowed, so as not to break upgrade paths from non-standard bundled plugins using uppercase
1444
+ * characters in the plugin directory path/slug. Silly them.
1445
+ *
1446
+ * @see https://developer.wordpress.org/reference/hooks/sanitize_key/
1447
+ *
1448
+ * @since 2.5.0
1449
+ *
1450
+ * @param string $key String key.
1451
+ * @return string Sanitized key
1452
+ */
1453
+ public function sanitize_key( $key ) {
1454
+ $raw_key = $key;
1455
+ $key = preg_replace( '`[^A-Za-z0-9_-]`', '', $key );
1456
+
1457
+ /**
1458
+ * Filter a sanitized key string.
1459
+ *
1460
+ * @since 2.5.0
1461
+ *
1462
+ * @param string $key Sanitized key.
1463
+ * @param string $raw_key The key prior to sanitization.
1464
+ */
1465
+ return apply_filters( 'tgmpa_sanitize_key', $key, $raw_key );
1466
+ }
1467
+
1468
+ /**
1469
+ * Amend default configuration settings.
1470
+ *
1471
+ * @since 2.0.0
1472
+ *
1473
+ * @param array $config Array of config options to pass as class properties.
1474
+ */
1475
+ public function config( $config ) {
1476
+ $keys = array(
1477
+ 'id',
1478
+ 'default_path',
1479
+ 'has_notices',
1480
+ 'dismissable',
1481
+ 'dismiss_msg',
1482
+ 'menu',
1483
+ 'parent_slug',
1484
+ 'capability',
1485
+ 'is_automatic',
1486
+ 'message',
1487
+ 'strings',
1488
+ );
1489
+
1490
+ foreach ( $keys as $key ) {
1491
+ if ( isset( $config[ $key ] ) ) {
1492
+ if ( is_array( $config[ $key ] ) ) {
1493
+ $this->$key = array_merge( $this->$key, $config[ $key ] );
1494
+ } else {
1495
+ $this->$key = $config[ $key ];
1496
+ }
1497
+ }
1498
+ }
1499
+ }
1500
+
1501
+ /**
1502
+ * Amend action link after plugin installation.
1503
+ *
1504
+ * @since 2.0.0
1505
+ *
1506
+ * @param array $install_actions Existing array of actions.
1507
+ * @return false|array Amended array of actions.
1508
+ */
1509
+ public function actions( $install_actions ) {
1510
+ // Remove action links on the TGMPA install page.
1511
+ if ( $this->is_tgmpa_page() ) {
1512
+ return false;
1513
+ }
1514
+
1515
+ return $install_actions;
1516
+ }
1517
+
1518
+ /**
1519
+ * Flushes the plugins cache on theme switch to prevent stale entries
1520
+ * from remaining in the plugin table.
1521
+ *
1522
+ * @since 2.4.0
1523
+ *
1524
+ * @param bool $clear_update_cache Optional. Whether to clear the Plugin updates cache.
1525
+ * Parameter added in v2.5.0.
1526
+ */
1527
+ public function flush_plugins_cache( $clear_update_cache = true ) {
1528
+ wp_clean_plugins_cache( $clear_update_cache );
1529
+ }
1530
+
1531
+ /**
1532
+ * Set file_path key for each installed plugin.
1533
+ *
1534
+ * @since 2.1.0
1535
+ *
1536
+ * @param string $plugin_slug Optional. If set, only (re-)populates the file path for that specific plugin.
1537
+ * Parameter added in v2.5.0.
1538
+ */
1539
+ public function populate_file_path( $plugin_slug = '' ) {
1540
+ if ( ! empty( $plugin_slug ) && is_string( $plugin_slug ) && isset( $this->plugins[ $plugin_slug ] ) ) {
1541
+ $this->plugins[ $plugin_slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $plugin_slug );
1542
+ } else {
1543
+ // Add file_path key for all plugins.
1544
+ foreach ( $this->plugins as $slug => $values ) {
1545
+ $this->plugins[ $slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $slug );
1546
+ }
1547
+ }
1548
+ }
1549
+
1550
+ /**
1551
+ * Helper function to extract the file path of the plugin file from the
1552
+ * plugin slug, if the plugin is installed.
1553
+ *
1554
+ * @since 2.0.0
1555
+ *
1556
+ * @param string $slug Plugin slug (typically folder name) as provided by the developer.
1557
+ * @return string Either file path for plugin if installed, or just the plugin slug.
1558
+ */
1559
+ protected function _get_plugin_basename_from_slug( $slug ) {
1560
+ $keys = array_keys( $this->get_plugins() );
1561
+
1562
+ foreach ( $keys as $key ) {
1563
+ if ( preg_match( '|^' . $slug . '/|', $key ) ) {
1564
+ return $key;
1565
+ }
1566
+ }
1567
+
1568
+ return $slug;
1569
+ }
1570
+
1571
+ /**
1572
+ * Retrieve plugin data, given the plugin name.
1573
+ *
1574
+ * Loops through the registered plugins looking for $name. If it finds it,
1575
+ * it returns the $data from that plugin. Otherwise, returns false.
1576
+ *
1577
+ * @since 2.1.0
1578
+ *
1579
+ * @param string $name Name of the plugin, as it was registered.
1580
+ * @param string $data Optional. Array key of plugin data to return. Default is slug.
1581
+ * @return string|boolean Plugin slug if found, false otherwise.
1582
+ */
1583
+ public function _get_plugin_data_from_name( $name, $data = 'slug' ) {
1584
+ foreach ( $this->plugins as $values ) {
1585
+ if ( $name === $values['name'] && isset( $values[ $data ] ) ) {
1586
+ return $values[ $data ];
1587
+ }
1588
+ }
1589
+
1590
+ return false;
1591
+ }
1592
+
1593
+ /**
1594
+ * Retrieve the download URL for a package.
1595
+ *
1596
+ * @since 2.5.0
1597
+ *
1598
+ * @param string $slug Plugin slug.
1599
+ * @return string Plugin download URL or path to local file or empty string if undetermined.
1600
+ */
1601
+ public function get_download_url( $slug ) {
1602
+ $dl_source = '';
1603
+
1604
+ switch ( $this->plugins[ $slug ]['source_type'] ) {
1605
+ case 'repo':
1606
+ return $this->get_wp_repo_download_url( $slug );
1607
+ case 'external':
1608
+ return $this->plugins[ $slug ]['source'];
1609
+ case 'bundled':
1610
+ return $this->default_path . $this->plugins[ $slug ]['source'];
1611
+ }
1612
+
1613
+ return $dl_source; // Should never happen.
1614
+ }
1615
+
1616
+ /**
1617
+ * Retrieve the download URL for a WP repo package.
1618
+ *
1619
+ * @since 2.5.0
1620
+ *
1621
+ * @param string $slug Plugin slug.
1622
+ * @return string Plugin download URL.
1623
+ */
1624
+ protected function get_wp_repo_download_url( $slug ) {
1625
+ $source = '';
1626
+ $api = $this->get_plugins_api( $slug );
1627
+
1628
+ if ( false !== $api && isset( $api->download_link ) ) {
1629
+ $source = $api->download_link;
1630
+ }
1631
+
1632
+ return $source;
1633
+ }
1634
+
1635
+ /**
1636
+ * Try to grab information from WordPress API.
1637
+ *
1638
+ * @since 2.5.0
1639
+ *
1640
+ * @param string $slug Plugin slug.
1641
+ * @return object Plugins_api response object on success, WP_Error on failure.
1642
+ */
1643
+ protected function get_plugins_api( $slug ) {
1644
+ static $api = array(); // Cache received responses.
1645
+
1646
+ if ( ! isset( $api[ $slug ] ) ) {
1647
+ if ( ! function_exists( 'plugins_api' ) ) {
1648
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
1649
+ }
1650
+
1651
+ $response = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );
1652
+
1653
+ $api[ $slug ] = false;
1654
+
1655
+ if ( is_wp_error( $response ) ) {
1656
+ wp_die( esc_html( $this->strings['oops'] ) );
1657
+ } else {
1658
+ $api[ $slug ] = $response;
1659
+ }
1660
+ }
1661
+
1662
+ return $api[ $slug ];
1663
+ }
1664
+
1665
+ /**
1666
+ * Retrieve a link to a plugin information page.
1667
+ *
1668
+ * @since 2.5.0
1669
+ *
1670
+ * @param string $slug Plugin slug.
1671
+ * @return string Fully formed html link to a plugin information page if available
1672
+ * or the plugin name if not.
1673
+ */
1674
+ public function get_info_link( $slug ) {
1675
+ if ( ! empty( $this->plugins[ $slug ]['external_url'] ) && preg_match( self::IS_URL_REGEX, $this->plugins[ $slug ]['external_url'] ) ) {
1676
+ $link = sprintf(
1677
+ '<a href="%1$s" target="_blank">%2$s</a>',
1678
+ esc_url( $this->plugins[ $slug ]['external_url'] ),
1679
+ esc_html( $this->plugins[ $slug ]['name'] )
1680
+ );
1681
+ } elseif ( 'repo' === $this->plugins[ $slug ]['source_type'] ) {
1682
+ $url = add_query_arg(
1683
+ array(
1684
+ 'tab' => 'plugin-information',
1685
+ 'plugin' => urlencode( $slug ),
1686
+ 'TB_iframe' => 'true',
1687
+ 'width' => '640',
1688
+ 'height' => '500',
1689
+ ),
1690
+ self_admin_url( 'plugin-install.php' )
1691
+ );
1692
+
1693
+ $link = sprintf(
1694
+ '<a href="%1$s" class="thickbox">%2$s</a>',
1695
+ esc_url( $url ),
1696
+ esc_html( $this->plugins[ $slug ]['name'] )
1697
+ );
1698
+ } else {
1699
+ $link = esc_html( $this->plugins[ $slug ]['name'] ); // No hyperlink.
1700
+ }
1701
+
1702
+ return $link;
1703
+ }
1704
+
1705
+ /**
1706
+ * Determine if we're on the TGMPA Install page.
1707
+ *
1708
+ * @since 2.1.0
1709
+ *
1710
+ * @return boolean True when on the TGMPA page, false otherwise.
1711
+ */
1712
+ protected function is_tgmpa_page() {
1713
+ return isset( $_GET['page'] ) && $this->menu === $_GET['page'];
1714
+ }
1715
+
1716
+ /**
1717
+ * Determine if we're on a WP Core installation/upgrade page.
1718
+ *
1719
+ * @since 2.6.0
1720
+ *
1721
+ * @return boolean True when on a WP Core installation/upgrade page, false otherwise.
1722
+ */
1723
+ protected function is_core_update_page() {
1724
+ // Current screen is not always available, most notably on the customizer screen.
1725
+ if ( ! function_exists( 'get_current_screen' ) ) {
1726
+ return false;
1727
+ }
1728
+
1729
+ $screen = get_current_screen();
1730
+
1731
+ if ( 'update-core' === $screen->base ) {
1732
+ // Core update screen.
1733
+ return true;
1734
+ } elseif ( 'plugins' === $screen->base && ! empty( $_POST['action'] ) ) { // WPCS: CSRF ok.
1735
+ // Plugins bulk update screen.
1736
+ return true;
1737
+ } elseif ( 'update' === $screen->base && ! empty( $_POST['action'] ) ) { // WPCS: CSRF ok.
1738
+ // Individual updates (ajax call).
1739
+ return true;
1740
+ }
1741
+
1742
+ return false;
1743
+ }
1744
+
1745
+ /**
1746
+ * Retrieve the URL to the TGMPA Install page.
1747
+ *
1748
+ * I.e. depending on the config settings passed something along the lines of:
1749
+ * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins
1750
+ *
1751
+ * @since 2.5.0
1752
+ *
1753
+ * @return string Properly encoded URL (not escaped).
1754
+ */
1755
+ public function get_tgmpa_url() {
1756
+ static $url;
1757
+
1758
+ if ( ! isset( $url ) ) {
1759
+ $parent = $this->parent_slug;
1760
+ if ( false === strpos( $parent, '.php' ) ) {
1761
+ $parent = 'admin.php';
1762
+ }
1763
+ $url = add_query_arg(
1764
+ array(
1765
+ 'page' => urlencode( $this->menu ),
1766
+ ),
1767
+ self_admin_url( $parent )
1768
+ );
1769
+ }
1770
+
1771
+ return $url;
1772
+ }
1773
+
1774
+ /**
1775
+ * Retrieve the URL to the TGMPA Install page for a specific plugin status (view).
1776
+ *
1777
+ * I.e. depending on the config settings passed something along the lines of:
1778
+ * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins&plugin_status=install
1779
+ *
1780
+ * @since 2.5.0
1781
+ *
1782
+ * @param string $status Plugin status - either 'install', 'update' or 'activate'.
1783
+ * @return string Properly encoded URL (not escaped).
1784
+ */
1785
+ public function get_tgmpa_status_url( $status ) {
1786
+ return add_query_arg(
1787
+ array(
1788
+ 'plugin_status' => urlencode( $status ),
1789
+ ),
1790
+ $this->get_tgmpa_url()
1791
+ );
1792
+ }
1793
+
1794
+ /**
1795
+ * Determine whether there are open actions for plugins registered with TGMPA.
1796
+ *
1797
+ * @since 2.5.0
1798
+ *
1799
+ * @return bool True if complete, i.e. no outstanding actions. False otherwise.
1800
+ */
1801
+ public function is_tgmpa_complete() {
1802
+ $complete = true;
1803
+ foreach ( $this->plugins as $slug => $plugin ) {
1804
+ if ( ! $this->is_plugin_active( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1805
+ $complete = false;
1806
+ break;
1807
+ }
1808
+ }
1809
+
1810
+ return $complete;
1811
+ }
1812
+
1813
+ /**
1814
+ * Check if a plugin is installed. Does not take must-use plugins into account.
1815
+ *
1816
+ * @since 2.5.0
1817
+ *
1818
+ * @param string $slug Plugin slug.
1819
+ * @return bool True if installed, false otherwise.
1820
+ */
1821
+ public function is_plugin_installed( $slug ) {
1822
+ $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1823
+
1824
+ return ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ] ) );
1825
+ }
1826
+
1827
+ /**
1828
+ * Check if a plugin is active.
1829
+ *
1830
+ * @since 2.5.0
1831
+ *
1832
+ * @param string $slug Plugin slug.
1833
+ * @return bool True if active, false otherwise.
1834
+ */
1835
+ public function is_plugin_active( $slug ) {
1836
+ return ( ( ! empty( $this->plugins[ $slug ]['is_callable'] ) && is_callable( $this->plugins[ $slug ]['is_callable'] ) ) || is_plugin_active( $this->plugins[ $slug ]['file_path'] ) );
1837
+ }
1838
+
1839
+ /**
1840
+ * Check if a plugin can be updated, i.e. if we have information on the minimum WP version required
1841
+ * available, check whether the current install meets them.
1842
+ *
1843
+ * @since 2.5.0
1844
+ *
1845
+ * @param string $slug Plugin slug.
1846
+ * @return bool True if OK to update, false otherwise.
1847
+ */
1848
+ public function can_plugin_update( $slug ) {
1849
+ // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1850
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1851
+ return true;
1852
+ }
1853
+
1854
+ $api = $this->get_plugins_api( $slug );
1855
+
1856
+ if ( false !== $api && isset( $api->requires ) ) {
1857
+ return version_compare( $this->wp_version, $api->requires, '>=' );
1858
+ }
1859
+
1860
+ // No usable info received from the plugins API, presume we can update.
1861
+ return true;
1862
+ }
1863
+
1864
+ /**
1865
+ * Check to see if the plugin is 'updatetable', i.e. installed, with an update available
1866
+ * and no WP version requirements blocking it.
1867
+ *
1868
+ * @since 2.6.0
1869
+ *
1870
+ * @param string $slug Plugin slug.
1871
+ * @return bool True if OK to proceed with update, false otherwise.
1872
+ */
1873
+ public function is_plugin_updatetable( $slug ) {
1874
+ if ( ! $this->is_plugin_installed( $slug ) ) {
1875
+ return false;
1876
+ } else {
1877
+ return ( false !== $this->does_plugin_have_update( $slug ) && $this->can_plugin_update( $slug ) );
1878
+ }
1879
+ }
1880
+
1881
+ /**
1882
+ * Check if a plugin can be activated, i.e. is not currently active and meets the minimum
1883
+ * plugin version requirements set in TGMPA (if any).
1884
+ *
1885
+ * @since 2.5.0
1886
+ *
1887
+ * @param string $slug Plugin slug.
1888
+ * @return bool True if OK to activate, false otherwise.
1889
+ */
1890
+ public function can_plugin_activate( $slug ) {
1891
+ return ( ! $this->is_plugin_active( $slug ) && ! $this->does_plugin_require_update( $slug ) );
1892
+ }
1893
+
1894
+ /**
1895
+ * Retrieve the version number of an installed plugin.
1896
+ *
1897
+ * @since 2.5.0
1898
+ *
1899
+ * @param string $slug Plugin slug.
1900
+ * @return string Version number as string or an empty string if the plugin is not installed
1901
+ * or version unknown (plugins which don't comply with the plugin header standard).
1902
+ */
1903
+ public function get_installed_version( $slug ) {
1904
+ $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1905
+
1906
+ if ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'] ) ) {
1907
+ return $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'];
1908
+ }
1909
+
1910
+ return '';
1911
+ }
1912
+
1913
+ /**
1914
+ * Check whether a plugin complies with the minimum version requirements.
1915
+ *
1916
+ * @since 2.5.0
1917
+ *
1918
+ * @param string $slug Plugin slug.
1919
+ * @return bool True when a plugin needs to be updated, otherwise false.
1920
+ */
1921
+ public function does_plugin_require_update( $slug ) {
1922
+ $installed_version = $this->get_installed_version( $slug );
1923
+ $minimum_version = $this->plugins[ $slug ]['version'];
1924
+
1925
+ return version_compare( $minimum_version, $installed_version, '>' );
1926
+ }
1927
+
1928
+ /**
1929
+ * Check whether there is an update available for a plugin.
1930
+ *
1931
+ * @since 2.5.0
1932
+ *
1933
+ * @param string $slug Plugin slug.
1934
+ * @return false|string Version number string of the available update or false if no update available.
1935
+ */
1936
+ public function does_plugin_have_update( $slug ) {
1937
+ // Presume bundled and external plugins will point to a package which meets the minimum required version.
1938
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1939
+ if ( $this->does_plugin_require_update( $slug ) ) {
1940
+ return $this->plugins[ $slug ]['version'];
1941
+ }
1942
+
1943
+ return false;
1944
+ }
1945
+
1946
+ $repo_updates = get_site_transient( 'update_plugins' );
1947
+
1948
+ if ( isset( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version ) ) {
1949
+ return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version;
1950
+ }
1951
+
1952
+ return false;
1953
+ }
1954
+
1955
+ /**
1956
+ * Retrieve potential upgrade notice for a plugin.
1957
+ *
1958
+ * @since 2.5.0
1959
+ *
1960
+ * @param string $slug Plugin slug.
1961
+ * @return string The upgrade notice or an empty string if no message was available or provided.
1962
+ */
1963
+ public function get_upgrade_notice( $slug ) {
1964
+ // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1965
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1966
+ return '';
1967
+ }
1968
+
1969
+ $repo_updates = get_site_transient( 'update_plugins' );
1970
+
1971
+ if ( ! empty( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice ) ) {
1972
+ return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice;
1973
+ }
1974
+
1975
+ return '';
1976
+ }
1977
+
1978
+ /**
1979
+ * Wrapper around the core WP get_plugins function, making sure it's actually available.
1980
+ *
1981
+ * @since 2.5.0
1982
+ *
1983
+ * @param string $plugin_folder Optional. Relative path to single plugin folder.
1984
+ * @return array Array of installed plugins with plugin information.
1985
+ */
1986
+ public function get_plugins( $plugin_folder = '' ) {
1987
+ if ( ! function_exists( 'get_plugins' ) ) {
1988
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
1989
+ }
1990
+
1991
+ return get_plugins( $plugin_folder );
1992
+ }
1993
+
1994
+ /**
1995
+ * Delete dismissable nag option when theme is switched.
1996
+ *
1997
+ * This ensures that the user(s) is/are again reminded via nag of required
1998
+ * and/or recommended plugins if they re-activate the theme.
1999
+ *
2000
+ * @since 2.1.1
2001
+ */
2002
+ public function update_dismiss() {
2003
+ delete_metadata( 'user', null, 'tgmpa_dismissed_notice_' . $this->id, null, true );
2004
+ }
2005
+
2006
+ /**
2007
+ * Forces plugin activation if the parameter 'force_activation' is
2008
+ * set to true.
2009
+ *
2010
+ * This allows theme authors to specify certain plugins that must be
2011
+ * active at all times while using the current theme.
2012
+ *
2013
+ * Please take special care when using this parameter as it has the
2014
+ * potential to be harmful if not used correctly. Setting this parameter
2015
+ * to true will not allow the specified plugin to be deactivated unless
2016
+ * the user switches themes.
2017
+ *
2018
+ * @since 2.2.0
2019
+ */
2020
+ public function force_activation() {
2021
+ foreach ( $this->plugins as $slug => $plugin ) {
2022
+ if ( true === $plugin['force_activation'] ) {
2023
+ if ( ! $this->is_plugin_installed( $slug ) ) {
2024
+ // Oops, plugin isn't there so iterate to next condition.
2025
+ continue;
2026
+ } elseif ( $this->can_plugin_activate( $slug ) ) {
2027
+ // There we go, activate the plugin.
2028
+ activate_plugin( $plugin['file_path'] );
2029
+ }
2030
+ }
2031
+ }
2032
+ }
2033
+
2034
+ /**
2035
+ * Forces plugin deactivation if the parameter 'force_deactivation'
2036
+ * is set to true and adds the plugin to the 'recently active' plugins list.
2037
+ *
2038
+ * This allows theme authors to specify certain plugins that must be
2039
+ * deactivated upon switching from the current theme to another.
2040
+ *
2041
+ * Please take special care when using this parameter as it has the
2042
+ * potential to be harmful if not used correctly.
2043
+ *
2044
+ * @since 2.2.0
2045
+ */
2046
+ public function force_deactivation() {
2047
+ $deactivated = array();
2048
+
2049
+ foreach ( $this->plugins as $slug => $plugin ) {
2050
+ /*
2051
+ * Only proceed forward if the parameter is set to true and plugin is active
2052
+ * as a 'normal' (not must-use) plugin.
2053
+ */
2054
+ if ( true === $plugin['force_deactivation'] && is_plugin_active( $plugin['file_path'] ) ) {
2055
+ deactivate_plugins( $plugin['file_path'] );
2056
+ $deactivated[ $plugin['file_path'] ] = time();
2057
+ }
2058
+ }
2059
+
2060
+ if ( ! empty( $deactivated ) ) {
2061
+ update_option( 'recently_activated', $deactivated + (array) get_option( 'recently_activated' ) );
2062
+ }
2063
+ }
2064
+
2065
+ /**
2066
+ * Echo the current TGMPA version number to the page.
2067
+ *
2068
+ * @since 2.5.0
2069
+ */
2070
+ public function show_tgmpa_version() {
2071
+ echo '<p style="float: right; padding: 0em 1.5em 0.5em 0;"><strong><small>',
2072
+ esc_html(
2073
+ sprintf(
2074
+ /* translators: %s: version number */
2075
+ __( 'TGMPA v%s', 'tgmpa' ),
2076
+ self::TGMPA_VERSION
2077
+ )
2078
+ ),
2079
+ '</small></strong></p>';
2080
+ }
2081
+
2082
+ /**
2083
+ * Returns the singleton instance of the class.
2084
+ *
2085
+ * @since 2.4.0
2086
+ *
2087
+ * @return \TGM_Plugin_Activation The TGM_Plugin_Activation object.
2088
+ */
2089
+ public static function get_instance() {
2090
+ if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
2091
+ self::$instance = new self();
2092
+ }
2093
+
2094
+ return self::$instance;
2095
+ }
2096
+ }
2097
+
2098
+ if ( ! function_exists( 'load_tgm_plugin_activation' ) ) {
2099
+ /**
2100
+ * Ensure only one instance of the class is ever invoked.
2101
+ *
2102
+ * @since 2.5.0
2103
+ */
2104
+ function load_tgm_plugin_activation() {
2105
+ $GLOBALS['tgmpa'] = TGM_Plugin_Activation::get_instance();
2106
+ }
2107
+ }
2108
+
2109
+ if ( did_action( 'plugins_loaded' ) ) {
2110
+ load_tgm_plugin_activation();
2111
+ } else {
2112
+ add_action( 'plugins_loaded', 'load_tgm_plugin_activation' );
2113
+ }
2114
+ }
2115
+
2116
+ if ( ! function_exists( 'tgmpa' ) ) {
2117
+ /**
2118
+ * Helper function to register a collection of required plugins.
2119
+ *
2120
+ * @since 2.0.0
2121
+ * @api
2122
+ *
2123
+ * @param array $plugins An array of plugin arrays.
2124
+ * @param array $config Optional. An array of configuration values.
2125
+ */
2126
+ function tgmpa( $plugins, $config = array() ) {
2127
+ $instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
2128
+
2129
+ foreach ( $plugins as $plugin ) {
2130
+ call_user_func( array( $instance, 'register' ), $plugin );
2131
+ }
2132
+
2133
+ if ( ! empty( $config ) && is_array( $config ) ) {
2134
+ // Send out notices for deprecated arguments passed.
2135
+ if ( isset( $config['notices'] ) ) {
2136
+ _deprecated_argument( __FUNCTION__, '2.2.0', 'The `notices` config parameter was renamed to `has_notices` in TGMPA 2.2.0. Please adjust your configuration.' );
2137
+ if ( ! isset( $config['has_notices'] ) ) {
2138
+ $config['has_notices'] = $config['notices'];
2139
+ }
2140
+ }
2141
+
2142
+ if ( isset( $config['parent_menu_slug'] ) ) {
2143
+ _deprecated_argument( __FUNCTION__, '2.4.0', 'The `parent_menu_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.' );
2144
+ }
2145
+ if ( isset( $config['parent_url_slug'] ) ) {
2146
+ _deprecated_argument( __FUNCTION__, '2.4.0', 'The `parent_url_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.' );
2147
+ }
2148
+
2149
+ call_user_func( array( $instance, 'config' ), $config );
2150
+ }
2151
+ }
2152
+ }
2153
+
2154
+ /**
2155
+ * WP_List_Table isn't always available. If it isn't available,
2156
+ * we load it here.
2157
+ *
2158
+ * @since 2.2.0
2159
+ */
2160
+ if ( ! class_exists( 'WP_List_Table' ) ) {
2161
+ require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
2162
+ }
2163
+
2164
+ if ( ! class_exists( 'TGMPA_List_Table' ) ) {
2165
+
2166
+ /**
2167
+ * List table class for handling plugins.
2168
+ *
2169
+ * Extends the WP_List_Table class to provide a future-compatible
2170
+ * way of listing out all required/recommended plugins.
2171
+ *
2172
+ * Gives users an interface similar to the Plugin Administration
2173
+ * area with similar (albeit stripped down) capabilities.
2174
+ *
2175
+ * This class also allows for the bulk install of plugins.
2176
+ *
2177
+ * @since 2.2.0
2178
+ *
2179
+ * @package TGM-Plugin-Activation
2180
+ * @author Thomas Griffin
2181
+ * @author Gary Jones
2182
+ */
2183
+ class TGMPA_List_Table extends WP_List_Table {
2184
+ /**
2185
+ * TGMPA instance.
2186
+ *
2187
+ * @since 2.5.0
2188
+ *
2189
+ * @var object
2190
+ */
2191
+ protected $tgmpa;
2192
+
2193
+ /**
2194
+ * The currently chosen view.
2195
+ *
2196
+ * @since 2.5.0
2197
+ *
2198
+ * @var string One of: 'all', 'install', 'update', 'activate'
2199
+ */
2200
+ public $view_context = 'all';
2201
+
2202
+ /**
2203
+ * The plugin counts for the various views.
2204
+ *
2205
+ * @since 2.5.0
2206
+ *
2207
+ * @var array
2208
+ */
2209
+ protected $view_totals = array(
2210
+ 'all' => 0,
2211
+ 'install' => 0,
2212
+ 'update' => 0,
2213
+ 'activate' => 0,
2214
+ );
2215
+
2216
+ /**
2217
+ * References parent constructor and sets defaults for class.
2218
+ *
2219
+ * @since 2.2.0
2220
+ */
2221
+ public function __construct() {
2222
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
2223
+
2224
+ parent::__construct(
2225
+ array(
2226
+ 'singular' => 'plugin',
2227
+ 'plural' => 'plugins',
2228
+ 'ajax' => false,
2229
+ )
2230
+ );
2231
+
2232
+ if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'install', 'update', 'activate' ), true ) ) {
2233
+ $this->view_context = sanitize_key( $_REQUEST['plugin_status'] );
2234
+ }
2235
+
2236
+ add_filter( 'tgmpa_table_data_items', array( $this, 'sort_table_items' ) );
2237
+ }
2238
+
2239
+ /**
2240
+ * Get a list of CSS classes for the <table> tag.
2241
+ *
2242
+ * Overruled to prevent the 'plural' argument from being added.
2243
+ *
2244
+ * @since 2.5.0
2245
+ *
2246
+ * @return array CSS classnames.
2247
+ */
2248
+ public function get_table_classes() {
2249
+ return array( 'widefat', 'fixed' );
2250
+ }
2251
+
2252
+ /**
2253
+ * Gathers and renames all of our plugin information to be used by WP_List_Table to create our table.
2254
+ *
2255
+ * @since 2.2.0
2256
+ *
2257
+ * @return array $table_data Information for use in table.
2258
+ */
2259
+ protected function _gather_plugin_data() {
2260
+ // Load thickbox for plugin links.
2261
+ $this->tgmpa->admin_init();
2262
+ $this->tgmpa->thickbox();
2263
+
2264
+ // Categorize the plugins which have open actions.
2265
+ $plugins = $this->categorize_plugins_to_views();
2266
+
2267
+ // Set the counts for the view links.
2268
+ $this->set_view_totals( $plugins );
2269
+
2270
+ // Prep variables for use and grab list of all installed plugins.
2271
+ $table_data = array();
2272
+ $i = 0;
2273
+
2274
+ // Redirect to the 'all' view if no plugins were found for the selected view context.
2275
+ if ( empty( $plugins[ $this->view_context ] ) ) {
2276
+ $this->view_context = 'all';
2277
+ }
2278
+
2279
+ foreach ( $plugins[ $this->view_context ] as $slug => $plugin ) {
2280
+ $table_data[ $i ]['sanitized_plugin'] = $plugin['name'];
2281
+ $table_data[ $i ]['slug'] = $slug;
2282
+ $table_data[ $i ]['plugin'] = '<strong>' . $this->tgmpa->get_info_link( $slug ) . '</strong>';
2283
+ $table_data[ $i ]['source'] = $this->get_plugin_source_type_text( $plugin['source_type'] );
2284
+ $table_data[ $i ]['type'] = $this->get_plugin_advise_type_text( $plugin['required'] );
2285
+ $table_data[ $i ]['status'] = $this->get_plugin_status_text( $slug );
2286
+ $table_data[ $i ]['installed_version'] = $this->tgmpa->get_installed_version( $slug );
2287
+ $table_data[ $i ]['minimum_version'] = $plugin['version'];
2288
+ $table_data[ $i ]['available_version'] = $this->tgmpa->does_plugin_have_update( $slug );
2289
+
2290
+ // Prep the upgrade notice info.
2291
+ $upgrade_notice = $this->tgmpa->get_upgrade_notice( $slug );
2292
+ if ( ! empty( $upgrade_notice ) ) {
2293
+ $table_data[ $i ]['upgrade_notice'] = $upgrade_notice;
2294
+
2295
+ add_action( "tgmpa_after_plugin_row_{$slug}", array( $this, 'wp_plugin_update_row' ), 10, 2 );
2296
+ }
2297
+
2298
+ $table_data[ $i ] = apply_filters( 'tgmpa_table_data_item', $table_data[ $i ], $plugin );
2299
+
2300
+ $i++;
2301
+ }
2302
+
2303
+ return $table_data;
2304
+ }
2305
+
2306
+ /**
2307
+ * Categorize the plugins which have open actions into views for the TGMPA page.
2308
+ *
2309
+ * @since 2.5.0
2310
+ */
2311
+ protected function categorize_plugins_to_views() {
2312
+ $plugins = array(
2313
+ 'all' => array(), // Meaning: all plugins which still have open actions.
2314
+ 'install' => array(),
2315
+ 'update' => array(),
2316
+ 'activate' => array(),
2317
+ );
2318
+
2319
+ foreach ( $this->tgmpa->plugins as $slug => $plugin ) {
2320
+ if ( $this->tgmpa->is_plugin_active( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2321
+ // No need to display plugins if they are installed, up-to-date and active.
2322
+ continue;
2323
+ } else {
2324
+ $plugins['all'][ $slug ] = $plugin;
2325
+
2326
+ if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2327
+ $plugins['install'][ $slug ] = $plugin;
2328
+ } else {
2329
+ if ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2330
+ $plugins['update'][ $slug ] = $plugin;
2331
+ }
2332
+
2333
+ if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
2334
+ $plugins['activate'][ $slug ] = $plugin;
2335
+ }
2336
+ }
2337
+ }
2338
+ }
2339
+
2340
+ return $plugins;
2341
+ }
2342
+
2343
+ /**
2344
+ * Set the counts for the view links.
2345
+ *
2346
+ * @since 2.5.0
2347
+ *
2348
+ * @param array $plugins Plugins order by view.
2349
+ */
2350
+ protected function set_view_totals( $plugins ) {
2351
+ foreach ( $plugins as $type => $list ) {
2352
+ $this->view_totals[ $type ] = count( $list );
2353
+ }
2354
+ }
2355
+
2356
+ /**
2357
+ * Get the plugin required/recommended text string.
2358
+ *
2359
+ * @since 2.5.0
2360
+ *
2361
+ * @param string $required Plugin required setting.
2362
+ * @return string
2363
+ */
2364
+ protected function get_plugin_advise_type_text( $required ) {
2365
+ if ( true === $required ) {
2366
+ return __( 'Required', 'tgmpa' );
2367
+ }
2368
+
2369
+ return __( 'Recommended', 'tgmpa' );
2370
+ }
2371
+
2372
+ /**
2373
+ * Get the plugin source type text string.
2374
+ *
2375
+ * @since 2.5.0
2376
+ *
2377
+ * @param string $type Plugin type.
2378
+ * @return string
2379
+ */
2380
+ protected function get_plugin_source_type_text( $type ) {
2381
+ $string = '';
2382
+
2383
+ switch ( $type ) {
2384
+ case 'repo':
2385
+ $string = __( 'WordPress Repository', 'tgmpa' );
2386
+ break;
2387
+ case 'external':
2388
+ $string = __( 'External Source', 'tgmpa' );
2389
+ break;
2390
+ case 'bundled':
2391
+ $string = __( 'Pre-Packaged', 'tgmpa' );
2392
+ break;
2393
+ }
2394
+
2395
+ return $string;
2396
+ }
2397
+
2398
+ /**
2399
+ * Determine the plugin status message.
2400
+ *
2401
+ * @since 2.5.0
2402
+ *
2403
+ * @param string $slug Plugin slug.
2404
+ * @return string
2405
+ */
2406
+ protected function get_plugin_status_text( $slug ) {
2407
+ if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2408
+ return __( 'Not Installed', 'tgmpa' );
2409
+ }
2410
+
2411
+ if ( ! $this->tgmpa->is_plugin_active( $slug ) ) {
2412
+ $install_status = __( 'Installed But Not Activated', 'tgmpa' );
2413
+ } else {
2414
+ $install_status = __( 'Active', 'tgmpa' );
2415
+ }
2416
+
2417
+ $update_status = '';
2418
+
2419
+ if ( $this->tgmpa->does_plugin_require_update( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2420
+ $update_status = __( 'Required Update not Available', 'tgmpa' );
2421
+
2422
+ } elseif ( $this->tgmpa->does_plugin_require_update( $slug ) ) {
2423
+ $update_status = __( 'Requires Update', 'tgmpa' );
2424
+
2425
+ } elseif ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2426
+ $update_status = __( 'Update recommended', 'tgmpa' );
2427
+ }
2428
+
2429
+ if ( '' === $update_status ) {
2430
+ return $install_status;
2431
+ }
2432
+
2433
+ return sprintf(
2434
+ /* translators: 1: install status, 2: update status */
2435
+ _x( '%1$s, %2$s', 'Install/Update Status', 'tgmpa' ),
2436
+ $install_status,
2437
+ $update_status
2438
+ );
2439
+ }
2440
+
2441
+ /**
2442
+ * Sort plugins by Required/Recommended type and by alphabetical plugin name within each type.
2443
+ *
2444
+ * @since 2.5.0
2445
+ *
2446
+ * @param array $items Prepared table items.
2447
+ * @return array Sorted table items.
2448
+ */
2449
+ public function sort_table_items( $items ) {
2450
+ $type = array();
2451
+ $name = array();
2452
+
2453
+ foreach ( $items as $i => $plugin ) {
2454
+ $type[ $i ] = $plugin['type']; // Required / recommended.
2455
+ $name[ $i ] = $plugin['sanitized_plugin'];
2456
+ }
2457
+
2458
+ array_multisort( $type, SORT_DESC, $name, SORT_ASC, $items );
2459
+
2460
+ return $items;
2461
+ }
2462
+
2463
+ /**
2464
+ * Get an associative array ( id => link ) of the views available on this table.
2465
+ *
2466
+ * @since 2.5.0
2467
+ *
2468
+ * @return array
2469
+ */
2470
+ public function get_views() {
2471
+ $status_links = array();
2472
+
2473
+ foreach ( $this->view_totals as $type => $count ) {
2474
+ if ( $count < 1 ) {
2475
+ continue;
2476
+ }
2477
+
2478
+ switch ( $type ) {
2479
+ case 'all':
2480
+ /* translators: 1: number of plugins. */
2481
+ $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins', 'tgmpa' );
2482
+ break;
2483
+ case 'install':
2484
+ /* translators: 1: number of plugins. */
2485
+ $text = _n( 'To Install <span class="count">(%s)</span>', 'To Install <span class="count">(%s)</span>', $count, 'tgmpa' );
2486
+ break;
2487
+ case 'update':
2488
+ /* translators: 1: number of plugins. */
2489
+ $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count, 'tgmpa' );
2490
+ break;
2491
+ case 'activate':
2492
+ /* translators: 1: number of plugins. */
2493
+ $text = _n( 'To Activate <span class="count">(%s)</span>', 'To Activate <span class="count">(%s)</span>', $count, 'tgmpa' );
2494
+ break;
2495
+ default:
2496
+ $text = '';
2497
+ break;
2498
+ }
2499
+
2500
+ if ( ! empty( $text ) ) {
2501
+
2502
+ $status_links[ $type ] = sprintf(
2503
+ '<a href="%s"%s>%s</a>',
2504
+ esc_url( $this->tgmpa->get_tgmpa_status_url( $type ) ),
2505
+ ( $type === $this->view_context ) ? ' class="current"' : '',
2506
+ sprintf( $text, number_format_i18n( $count ) )
2507
+ );
2508
+ }
2509
+ }
2510
+
2511
+ return $status_links;
2512
+ }
2513
+
2514
+ /**
2515
+ * Create default columns to display important plugin information
2516
+ * like type, action and status.
2517
+ *
2518
+ * @since 2.2.0
2519
+ *
2520
+ * @param array $item Array of item data.
2521
+ * @param string $column_name The name of the column.
2522
+ * @return string
2523
+ */
2524
+ public function column_default( $item, $column_name ) {
2525
+ return $item[ $column_name ];
2526
+ }
2527
+
2528
+ /**
2529
+ * Required for bulk installing.
2530
+ *
2531
+ * Adds a checkbox for each plugin.
2532
+ *
2533
+ * @since 2.2.0
2534
+ *
2535
+ * @param array $item Array of item data.
2536
+ * @return string The input checkbox with all necessary info.
2537
+ */
2538
+ public function column_cb( $item ) {
2539
+ return sprintf(
2540
+ '<input type="checkbox" name="%1$s[]" value="%2$s" id="%3$s" />',
2541
+ esc_attr( $this->_args['singular'] ),
2542
+ esc_attr( $item['slug'] ),
2543
+ esc_attr( $item['sanitized_plugin'] )
2544
+ );
2545
+ }
2546
+
2547
+ /**
2548
+ * Create default title column along with the action links.
2549
+ *
2550
+ * @since 2.2.0
2551
+ *
2552
+ * @param array $item Array of item data.
2553
+ * @return string The plugin name and action links.
2554
+ */
2555
+ public function column_plugin( $item ) {
2556
+ return sprintf(
2557
+ '%1$s %2$s',
2558
+ $item['plugin'],
2559
+ $this->row_actions( $this->get_row_actions( $item ), true )
2560
+ );
2561
+ }
2562
+
2563
+ /**
2564
+ * Create version information column.
2565
+ *
2566
+ * @since 2.5.0
2567
+ *
2568
+ * @param array $item Array of item data.
2569
+ * @return string HTML-formatted version information.
2570
+ */
2571
+ public function column_version( $item ) {
2572
+ $output = array();
2573
+
2574
+ if ( $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2575
+ $installed = ! empty( $item['installed_version'] ) ? $item['installed_version'] : _x( 'unknown', 'as in: "version nr unknown"', 'tgmpa' );
2576
+
2577
+ $color = '';
2578
+ if ( ! empty( $item['minimum_version'] ) && $this->tgmpa->does_plugin_require_update( $item['slug'] ) ) {
2579
+ $color = ' color: #ff0000; font-weight: bold;';
2580
+ }
2581
+
2582
+ $output[] = sprintf(
2583
+ '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Installed version:', 'tgmpa' ) . '</p>',
2584
+ $color,
2585
+ $installed
2586
+ );
2587
+ }
2588
+
2589
+ if ( ! empty( $item['minimum_version'] ) ) {
2590
+ $output[] = sprintf(
2591
+ '<p><span style="min-width: 32px; text-align: right; float: right;">%1$s</span>' . __( 'Minimum required version:', 'tgmpa' ) . '</p>',
2592
+ $item['minimum_version']
2593
+ );
2594
+ }
2595
+
2596
+ if ( ! empty( $item['available_version'] ) ) {
2597
+ $color = '';
2598
+ if ( ! empty( $item['minimum_version'] ) && version_compare( $item['available_version'], $item['minimum_version'], '>=' ) ) {
2599
+ $color = ' color: #71C671; font-weight: bold;';
2600
+ }
2601
+
2602
+ $output[] = sprintf(
2603
+ '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Available version:', 'tgmpa' ) . '</p>',
2604
+ $color,
2605
+ $item['available_version']
2606
+ );
2607
+ }
2608
+
2609
+ if ( empty( $output ) ) {
2610
+ return '&nbsp;'; // Let's not break the table layout.
2611
+ } else {
2612
+ return implode( "\n", $output );
2613
+ }
2614
+ }
2615
+
2616
+ /**
2617
+ * Sets default message within the plugins table if no plugins
2618
+ * are left for interaction.
2619
+ *
2620
+ * Hides the menu item to prevent the user from clicking and
2621
+ * getting a permissions error.
2622
+ *
2623
+ * @since 2.2.0
2624
+ */
2625
+ public function no_items() {
2626
+ echo esc_html__( 'No plugins to install, update or activate.', 'tgmpa' ) . ' <a href="' . esc_url( self_admin_url() ) . '"> ' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>';
2627
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
2628
+ }
2629
+
2630
+ /**
2631
+ * Output all the column information within the table.
2632
+ *
2633
+ * @since 2.2.0
2634
+ *
2635
+ * @return array $columns The column names.
2636
+ */
2637
+ public function get_columns() {
2638
+ $columns = array(
2639
+ 'cb' => '<input type="checkbox" />',
2640
+ 'plugin' => __( 'Plugin', 'tgmpa' ),
2641
+ 'source' => __( 'Source', 'tgmpa' ),
2642
+ 'type' => __( 'Type', 'tgmpa' ),
2643
+ );
2644
+
2645
+ if ( 'all' === $this->view_context || 'update' === $this->view_context ) {
2646
+ $columns['version'] = __( 'Version', 'tgmpa' );
2647
+ $columns['status'] = __( 'Status', 'tgmpa' );
2648
+ }
2649
+
2650
+ return apply_filters( 'tgmpa_table_columns', $columns );
2651
+ }
2652
+
2653
+ /**
2654
+ * Get name of default primary column
2655
+ *
2656
+ * @since 2.5.0 / WP 4.3+ compatibility
2657
+ * @access protected
2658
+ *
2659
+ * @return string
2660
+ */
2661
+ protected function get_default_primary_column_name() {
2662
+ return 'plugin';
2663
+ }
2664
+
2665
+ /**
2666
+ * Get the name of the primary column.
2667
+ *
2668
+ * @since 2.5.0 / WP 4.3+ compatibility
2669
+ * @access protected
2670
+ *
2671
+ * @return string The name of the primary column.
2672
+ */
2673
+ protected function get_primary_column_name() {
2674
+ if ( method_exists( 'WP_List_Table', 'get_primary_column_name' ) ) {
2675
+ return parent::get_primary_column_name();
2676
+ } else {
2677
+ return $this->get_default_primary_column_name();
2678
+ }
2679
+ }
2680
+
2681
+ /**
2682
+ * Get the actions which are relevant for a specific plugin row.
2683
+ *
2684
+ * @since 2.5.0
2685
+ *
2686
+ * @param array $item Array of item data.
2687
+ * @return array Array with relevant action links.
2688
+ */
2689
+ protected function get_row_actions( $item ) {
2690
+ $actions = array();
2691
+ $action_links = array();
2692
+
2693
+ // Display the 'Install' action link if the plugin is not yet available.
2694
+ if ( ! $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2695
+ /* translators: %2$s: plugin name in screen reader markup */
2696
+ $actions['install'] = __( 'Install %2$s', 'tgmpa' );
2697
+ } else {
2698
+ // Display the 'Update' action link if an update is available and WP complies with plugin minimum.
2699
+ if ( false !== $this->tgmpa->does_plugin_have_update( $item['slug'] ) && $this->tgmpa->can_plugin_update( $item['slug'] ) ) {
2700
+ /* translators: %2$s: plugin name in screen reader markup */
2701
+ $actions['update'] = __( 'Update %2$s', 'tgmpa' );
2702
+ }
2703
+
2704
+ // Display the 'Activate' action link, but only if the plugin meets the minimum version.
2705
+ if ( $this->tgmpa->can_plugin_activate( $item['slug'] ) ) {
2706
+ /* translators: %2$s: plugin name in screen reader markup */
2707
+ $actions['activate'] = __( 'Activate %2$s', 'tgmpa' );
2708
+ }
2709
+ }
2710
+
2711
+ // Create the actual links.
2712
+ foreach ( $actions as $action => $text ) {
2713
+ $nonce_url = wp_nonce_url(
2714
+ add_query_arg(
2715
+ array(
2716
+ 'plugin' => urlencode( $item['slug'] ),
2717
+ 'tgmpa-' . $action => $action . '-plugin',
2718
+ ),
2719
+ $this->tgmpa->get_tgmpa_url()
2720
+ ),
2721
+ 'tgmpa-' . $action,
2722
+ 'tgmpa-nonce'
2723
+ );
2724
+
2725
+ $action_links[ $action ] = sprintf(
2726
+ '<a href="%1$s">' . esc_html( $text ) . '</a>', // $text contains the second placeholder.
2727
+ esc_url( $nonce_url ),
2728
+ '<span class="screen-reader-text">' . esc_html( $item['sanitized_plugin'] ) . '</span>'
2729
+ );
2730
+ }
2731
+
2732
+ $prefix = ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) ? 'network_admin_' : '';
2733
+ return apply_filters( "tgmpa_{$prefix}plugin_action_links", array_filter( $action_links ), $item['slug'], $item, $this->view_context );
2734
+ }
2735
+
2736
+ /**
2737
+ * Generates content for a single row of the table.
2738
+ *
2739
+ * @since 2.5.0
2740
+ *
2741
+ * @param object $item The current item.
2742
+ */
2743
+ public function single_row( $item ) {
2744
+ parent::single_row( $item );
2745
+
2746
+ /**
2747
+ * Fires after each specific row in the TGMPA Plugins list table.
2748
+ *
2749
+ * The dynamic portion of the hook name, `$item['slug']`, refers to the slug
2750
+ * for the plugin.
2751
+ *
2752
+ * @since 2.5.0
2753
+ */
2754
+ do_action( "tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context );
2755
+ }
2756
+
2757
+ /**
2758
+ * Show the upgrade notice below a plugin row if there is one.
2759
+ *
2760
+ * @since 2.5.0
2761
+ *
2762
+ * @see /wp-admin/includes/update.php
2763
+ *
2764
+ * @param string $slug Plugin slug.
2765
+ * @param array $item The information available in this table row.
2766
+ * @return null Return early if upgrade notice is empty.
2767
+ */
2768
+ public function wp_plugin_update_row( $slug, $item ) {
2769
+ if ( empty( $item['upgrade_notice'] ) ) {
2770
+ return;
2771
+ }
2772
+
2773
+ echo '
2774
+ <tr class="plugin-update-tr">
2775
+ <td colspan="', absint( $this->get_column_count() ), '" class="plugin-update colspanchange">
2776
+ <div class="update-message">',
2777
+ esc_html__( 'Upgrade message from the plugin author:', 'tgmpa' ),
2778
+ ' <strong>', wp_kses_data( $item['upgrade_notice'] ), '</strong>
2779
+ </div>
2780
+ </td>
2781
+ </tr>';
2782
+ }
2783
+
2784
+ /**
2785
+ * Extra controls to be displayed between bulk actions and pagination.
2786
+ *
2787
+ * @since 2.5.0
2788
+ *
2789
+ * @param string $which 'top' or 'bottom' table navigation.
2790
+ */
2791
+ public function extra_tablenav( $which ) {
2792
+ if ( 'bottom' === $which ) {
2793
+ $this->tgmpa->show_tgmpa_version();
2794
+ }
2795
+ }
2796
+
2797
+ /**
2798
+ * Defines the bulk actions for handling registered plugins.
2799
+ *
2800
+ * @since 2.2.0
2801
+ *
2802
+ * @return array $actions The bulk actions for the plugin install table.
2803
+ */
2804
+ public function get_bulk_actions() {
2805
+
2806
+ $actions = array();
2807
+
2808
+ if ( 'update' !== $this->view_context && 'activate' !== $this->view_context ) {
2809
+ if ( current_user_can( 'install_plugins' ) ) {
2810
+ $actions['tgmpa-bulk-install'] = __( 'Install', 'tgmpa' );
2811
+ }
2812
+ }
2813
+
2814
+ if ( 'install' !== $this->view_context ) {
2815
+ if ( current_user_can( 'update_plugins' ) ) {
2816
+ $actions['tgmpa-bulk-update'] = __( 'Update', 'tgmpa' );
2817
+ }
2818
+ if ( current_user_can( 'activate_plugins' ) ) {
2819
+ $actions['tgmpa-bulk-activate'] = __( 'Activate', 'tgmpa' );
2820
+ }
2821
+ }
2822
+
2823
+ return $actions;
2824
+ }
2825
+
2826
+ /**
2827
+ * Processes bulk installation and activation actions.
2828
+ *
2829
+ * The bulk installation process looks for the $_POST information and passes that
2830
+ * through if a user has to use WP_Filesystem to enter their credentials.
2831
+ *
2832
+ * @since 2.2.0
2833
+ */
2834
+ public function process_bulk_actions() {
2835
+ // Bulk installation process.
2836
+ if ( 'tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action() ) {
2837
+
2838
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
2839
+
2840
+ $install_type = 'install';
2841
+ if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2842
+ $install_type = 'update';
2843
+ }
2844
+
2845
+ $plugins_to_install = array();
2846
+
2847
+ // Did user actually select any plugins to install/update ?
2848
+ if ( empty( $_POST['plugin'] ) ) {
2849
+ if ( 'install' === $install_type ) {
2850
+ $message = __( 'No plugins were selected to be installed. No action taken.', 'tgmpa' );
2851
+ } else {
2852
+ $message = __( 'No plugins were selected to be updated. No action taken.', 'tgmpa' );
2853
+ }
2854
+
2855
+ echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2856
+
2857
+ return false;
2858
+ }
2859
+
2860
+ if ( is_array( $_POST['plugin'] ) ) {
2861
+ $plugins_to_install = (array) $_POST['plugin'];
2862
+ } elseif ( is_string( $_POST['plugin'] ) ) {
2863
+ // Received via Filesystem page - un-flatten array (WP bug #19643).
2864
+ $plugins_to_install = explode( ',', $_POST['plugin'] );
2865
+ }
2866
+
2867
+ // Sanitize the received input.
2868
+ $plugins_to_install = array_map( 'urldecode', $plugins_to_install );
2869
+ $plugins_to_install = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins_to_install );
2870
+
2871
+ // Validate the received input.
2872
+ foreach ( $plugins_to_install as $key => $slug ) {
2873
+ // Check if the plugin was registered with TGMPA and remove if not.
2874
+ if ( ! isset( $this->tgmpa->plugins[ $slug ] ) ) {
2875
+ unset( $plugins_to_install[ $key ] );
2876
+ continue;
2877
+ }
2878
+
2879
+ // For install: make sure this is a plugin we *can* install and not one already installed.
2880
+ if ( 'install' === $install_type && true === $this->tgmpa->is_plugin_installed( $slug ) ) {
2881
+ unset( $plugins_to_install[ $key ] );
2882
+ }
2883
+
2884
+ // For updates: make sure this is a plugin we *can* update (update available and WP version ok).
2885
+ if ( 'update' === $install_type && false === $this->tgmpa->is_plugin_updatetable( $slug ) ) {
2886
+ unset( $plugins_to_install[ $key ] );
2887
+ }
2888
+ }
2889
+
2890
+ // No need to proceed further if we have no plugins to handle.
2891
+ if ( empty( $plugins_to_install ) ) {
2892
+ if ( 'install' === $install_type ) {
2893
+ $message = __( 'No plugins are available to be installed at this time.', 'tgmpa' );
2894
+ } else {
2895
+ $message = __( 'No plugins are available to be updated at this time.', 'tgmpa' );
2896
+ }
2897
+
2898
+ echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2899
+
2900
+ return false;
2901
+ }
2902
+
2903
+ // Pass all necessary information if WP_Filesystem is needed.
2904
+ $url = wp_nonce_url(
2905
+ $this->tgmpa->get_tgmpa_url(),
2906
+ 'bulk-' . $this->_args['plural']
2907
+ );
2908
+
2909
+ // Give validated data back to $_POST which is the only place the filesystem looks for extra fields.
2910
+ $_POST['plugin'] = implode( ',', $plugins_to_install ); // Work around for WP bug #19643.
2911
+
2912
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
2913
+ $fields = array_keys( $_POST ); // Extra fields to pass to WP_Filesystem.
2914
+
2915
+ if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, $fields ) ) ) {
2916
+ return true; // Stop the normal page form from displaying, credential request form will be shown.
2917
+ }
2918
+
2919
+ // Now we have some credentials, setup WP_Filesystem.
2920
+ if ( ! WP_Filesystem( $creds ) ) {
2921
+ // Our credentials were no good, ask the user for them again.
2922
+ request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, $fields );
2923
+
2924
+ return true;
2925
+ }
2926
+
2927
+ /* If we arrive here, we have the filesystem */
2928
+
2929
+ // Store all information in arrays since we are processing a bulk installation.
2930
+ $names = array();
2931
+ $sources = array(); // Needed for installs.
2932
+ $file_paths = array(); // Needed for upgrades.
2933
+ $to_inject = array(); // Information to inject into the update_plugins transient.
2934
+
2935
+ // Prepare the data for validated plugins for the install/upgrade.
2936
+ foreach ( $plugins_to_install as $slug ) {
2937
+ $name = $this->tgmpa->plugins[ $slug ]['name'];
2938
+ $source = $this->tgmpa->get_download_url( $slug );
2939
+
2940
+ if ( ! empty( $name ) && ! empty( $source ) ) {
2941
+ $names[] = $name;
2942
+
2943
+ switch ( $install_type ) {
2944
+
2945
+ case 'install':
2946
+ $sources[] = $source;
2947
+ break;
2948
+
2949
+ case 'update':
2950
+ $file_paths[] = $this->tgmpa->plugins[ $slug ]['file_path'];
2951
+ $to_inject[ $slug ] = $this->tgmpa->plugins[ $slug ];
2952
+ $to_inject[ $slug ]['source'] = $source;
2953
+ break;
2954
+ }
2955
+ }
2956
+ }
2957
+ unset( $slug, $name, $source );
2958
+
2959
+ // Create a new instance of TGMPA_Bulk_Installer.
2960
+ $installer = new TGMPA_Bulk_Installer(
2961
+ new TGMPA_Bulk_Installer_Skin(
2962
+ array(
2963
+ 'url' => esc_url_raw( $this->tgmpa->get_tgmpa_url() ),
2964
+ 'nonce' => 'bulk-' . $this->_args['plural'],
2965
+ 'names' => $names,
2966
+ 'install_type' => $install_type,
2967
+ )
2968
+ )
2969
+ );
2970
+
2971
+ // Wrap the install process with the appropriate HTML.
2972
+ echo '<div class="tgmpa">',
2973
+ '<h2 style="font-size: 23px; font-weight: 400; line-height: 29px; margin: 0; padding: 9px 15px 4px 0;">', esc_html( get_admin_page_title() ), '</h2>
2974
+ <div class="update-php" style="width: 100%; height: 98%; min-height: 850px; padding-top: 1px;">';
2975
+
2976
+ // Process the bulk installation submissions.
2977
+ add_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 );
2978
+
2979
+ if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2980
+ // Inject our info into the update transient.
2981
+ $this->tgmpa->inject_update_info( $to_inject );
2982
+
2983
+ $installer->bulk_upgrade( $file_paths );
2984
+ } else {
2985
+ $installer->bulk_install( $sources );
2986
+ }
2987
+
2988
+ remove_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1 );
2989
+
2990
+ echo '</div></div>';
2991
+
2992
+ return true;
2993
+ }
2994
+
2995
+ // Bulk activation process.
2996
+ if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
2997
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
2998
+
2999
+ // Did user actually select any plugins to activate ?
3000
+ if ( empty( $_POST['plugin'] ) ) {
3001
+ echo '<div id="message" class="error"><p>', esc_html__( 'No plugins were selected to be activated. No action taken.', 'tgmpa' ), '</p></div>';
3002
+
3003
+ return false;
3004
+ }
3005
+
3006
+ // Grab plugin data from $_POST.
3007
+ $plugins = array();
3008
+ if ( isset( $_POST['plugin'] ) ) {
3009
+ $plugins = array_map( 'urldecode', (array) $_POST['plugin'] );
3010
+ $plugins = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins );
3011
+ }
3012
+
3013
+ $plugins_to_activate = array();
3014
+ $plugin_names = array();
3015
+
3016
+ // Grab the file paths for the selected & inactive plugins from the registration array.
3017
+ foreach ( $plugins as $slug ) {
3018
+ if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
3019
+ $plugins_to_activate[] = $this->tgmpa->plugins[ $slug ]['file_path'];
3020
+ $plugin_names[] = $this->tgmpa->plugins[ $slug ]['name'];
3021
+ }
3022
+ }
3023
+ unset( $slug );
3024
+
3025
+ // Return early if there are no plugins to activate.
3026
+ if ( empty( $plugins_to_activate ) ) {
3027
+ echo '<div id="message" class="error"><p>', esc_html__( 'No plugins are available to be activated at this time.', 'tgmpa' ), '</p></div>';
3028
+
3029
+ return false;
3030
+ }
3031
+
3032
+ // Now we are good to go - let's start activating plugins.
3033
+ $activate = activate_plugins( $plugins_to_activate );
3034
+
3035
+ if ( is_wp_error( $activate ) ) {
3036
+ echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>';
3037
+ } else {
3038
+ $count = count( $plugin_names ); // Count so we can use _n function.
3039
+ $plugin_names = array_map( array( 'TGMPA_Utils', 'wrap_in_strong' ), $plugin_names );
3040
+ $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability.
3041
+ $imploded = empty( $plugin_names ) ? $last_plugin : ( implode( ', ', $plugin_names ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
3042
+
3043
+ printf( // WPCS: xss ok.
3044
+ '<div id="message" class="updated"><p>%1$s %2$s.</p></div>',
3045
+ esc_html( _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'tgmpa' ) ),
3046
+ $imploded
3047
+ );
3048
+
3049
+ // Update recently activated plugins option.
3050
+ $recent = (array) get_option( 'recently_activated' );
3051
+ foreach ( $plugins_to_activate as $plugin => $time ) {
3052
+ if ( isset( $recent[ $plugin ] ) ) {
3053
+ unset( $recent[ $plugin ] );
3054
+ }
3055
+ }
3056
+ update_option( 'recently_activated', $recent );
3057
+ }
3058
+
3059
+ unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another.
3060
+
3061
+ return true;
3062
+ }
3063
+
3064
+ return false;
3065
+ }
3066
+
3067
+ /**
3068
+ * Prepares all of our information to be outputted into a usable table.
3069
+ *
3070
+ * @since 2.2.0
3071
+ */
3072
+ public function prepare_items() {
3073
+ $columns = $this->get_columns(); // Get all necessary column information.
3074
+ $hidden = array(); // No columns to hide, but we must set as an array.
3075
+ $sortable = array(); // No reason to make sortable columns.
3076
+ $primary = $this->get_primary_column_name(); // Column which has the row actions.
3077
+ $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); // Get all necessary column headers.
3078
+
3079
+ // Process our bulk activations here.
3080
+ if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
3081
+ $this->process_bulk_actions();
3082
+ }
3083
+
3084
+ // Store all of our plugin data into $items array so WP_List_Table can use it.
3085
+ $this->items = apply_filters( 'tgmpa_table_data_items', $this->_gather_plugin_data() );
3086
+ }
3087
+
3088
+ /* *********** DEPRECATED METHODS *********** */
3089
+
3090
+ /**
3091
+ * Retrieve plugin data, given the plugin name.
3092
+ *
3093
+ * @since 2.2.0
3094
+ * @deprecated 2.5.0 use {@see TGM_Plugin_Activation::_get_plugin_data_from_name()} instead.
3095
+ * @see TGM_Plugin_Activation::_get_plugin_data_from_name()
3096
+ *
3097
+ * @param string $name Name of the plugin, as it was registered.
3098
+ * @param string $data Optional. Array key of plugin data to return. Default is slug.
3099
+ * @return string|boolean Plugin slug if found, false otherwise.
3100
+ */
3101
+ protected function _get_plugin_data_from_name( $name, $data = 'slug' ) {
3102
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'TGM_Plugin_Activation::_get_plugin_data_from_name()' );
3103
+
3104
+ return $this->tgmpa->_get_plugin_data_from_name( $name, $data );
3105
+ }
3106
+ }
3107
+ }
3108
+
3109
+
3110
+ if ( ! class_exists( 'TGM_Bulk_Installer' ) ) {
3111
+
3112
+ /**
3113
+ * Hack: Prevent TGMPA v2.4.1- bulk installer class from being loaded if 2.4.1- is loaded after 2.5+.
3114
+ *
3115
+ * @since 2.5.2
3116
+ *
3117
+ * {@internal The TGMPA_Bulk_Installer class was originally called TGM_Bulk_Installer.
3118
+ * For more information, see that class.}}
3119
+ */
3120
+ class TGM_Bulk_Installer {
3121
+ }
3122
+ }
3123
+ if ( ! class_exists( 'TGM_Bulk_Installer_Skin' ) ) {
3124
+
3125
+ /**
3126
+ * Hack: Prevent TGMPA v2.4.1- bulk installer skin class from being loaded if 2.4.1- is loaded after 2.5+.
3127
+ *
3128
+ * @since 2.5.2
3129
+ *
3130
+ * {@internal The TGMPA_Bulk_Installer_Skin class was originally called TGM_Bulk_Installer_Skin.
3131
+ * For more information, see that class.}}
3132
+ */
3133
+ class TGM_Bulk_Installer_Skin {
3134
+ }
3135
+ }
3136
+
3137
+ /**
3138
+ * The WP_Upgrader file isn't always available. If it isn't available,
3139
+ * we load it here.
3140
+ *
3141
+ * We check to make sure no action or activation keys are set so that WordPress
3142
+ * does not try to re-include the class when processing upgrades or installs outside
3143
+ * of the class.
3144
+ *
3145
+ * @since 2.2.0
3146
+ */
3147
+ add_action( 'admin_init', 'tgmpa_load_bulk_installer' );
3148
+ if ( ! function_exists( 'tgmpa_load_bulk_installer' ) ) {
3149
+ /**
3150
+ * Load bulk installer
3151
+ */
3152
+ function tgmpa_load_bulk_installer() {
3153
+ // Silently fail if 2.5+ is loaded *after* an older version.
3154
+ if ( ! isset( $GLOBALS['tgmpa'] ) ) {
3155
+ return;
3156
+ }
3157
+
3158
+ // Get TGMPA class instance.
3159
+ $tgmpa_instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
3160
+
3161
+ if ( isset( $_GET['page'] ) && $tgmpa_instance->menu === $_GET['page'] ) {
3162
+ if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
3163
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
3164
+ }
3165
+
3166
+ if ( ! class_exists( 'TGMPA_Bulk_Installer' ) ) {
3167
+
3168
+ /**
3169
+ * Installer class to handle bulk plugin installations.
3170
+ *
3171
+ * Extends WP_Upgrader and customizes to suit the installation of multiple
3172
+ * plugins.
3173
+ *
3174
+ * @since 2.2.0
3175
+ *
3176
+ * {@internal Since 2.5.0 the class is an extension of Plugin_Upgrader rather than WP_Upgrader.}}
3177
+ * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer to TGMPA_Bulk_Installer.
3178
+ * This was done to prevent backward compatibility issues with v2.3.6.}}
3179
+ *
3180
+ * @package TGM-Plugin-Activation
3181
+ * @author Thomas Griffin
3182
+ * @author Gary Jones
3183
+ */
3184
+ class TGMPA_Bulk_Installer extends Plugin_Upgrader {
3185
+ /**
3186
+ * Holds result of bulk plugin installation.
3187
+ *
3188
+ * @since 2.2.0
3189
+ *
3190
+ * @var string
3191
+ */
3192
+ public $result;
3193
+
3194
+ /**
3195
+ * Flag to check if bulk installation is occurring or not.
3196
+ *
3197
+ * @since 2.2.0
3198
+ *
3199
+ * @var boolean
3200
+ */
3201
+ public $bulk = false;
3202
+
3203
+ /**
3204
+ * TGMPA instance
3205
+ *
3206
+ * @since 2.5.0
3207
+ *
3208
+ * @var object
3209
+ */
3210
+ protected $tgmpa;
3211
+
3212
+ /**
3213
+ * Whether or not the destination directory needs to be cleared ( = on update).
3214
+ *
3215
+ * @since 2.5.0
3216
+ *
3217
+ * @var bool
3218
+ */
3219
+ protected $clear_destination = false;
3220
+
3221
+ /**
3222
+ * References parent constructor and sets defaults for class.
3223
+ *
3224
+ * @since 2.2.0
3225
+ *
3226
+ * @param \Bulk_Upgrader_Skin|null $skin Installer skin.
3227
+ */
3228
+ public function __construct( $skin = null ) {
3229
+ // Get TGMPA class instance.
3230
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
3231
+
3232
+ parent::__construct( $skin );
3233
+
3234
+ if ( isset( $this->skin->options['install_type'] ) && 'update' === $this->skin->options['install_type'] ) {
3235
+ $this->clear_destination = true;
3236
+ }
3237
+
3238
+ if ( $this->tgmpa->is_automatic ) {
3239
+ $this->activate_strings();
3240
+ }
3241
+
3242
+ add_action( 'upgrader_process_complete', array( $this->tgmpa, 'populate_file_path' ) );
3243
+ }
3244
+
3245
+ /**
3246
+ * Sets the correct activation strings for the installer skin to use.
3247
+ *
3248
+ * @since 2.2.0
3249
+ */
3250
+ public function activate_strings() {
3251
+ $this->strings['activation_failed'] = __( 'Plugin activation failed.', 'tgmpa' );
3252
+ $this->strings['activation_success'] = __( 'Plugin activated successfully.', 'tgmpa' );
3253
+ }
3254
+
3255
+ /**
3256
+ * Performs the actual installation of each plugin.
3257
+ *
3258
+ * @since 2.2.0
3259
+ *
3260
+ * @see WP_Upgrader::run()
3261
+ *
3262
+ * @param array $options The installation config options.
3263
+ * @return null|array Return early if error, array of installation data on success.
3264
+ */
3265
+ public function run( $options ) {
3266
+ $result = parent::run( $options );
3267
+
3268
+ // Reset the strings in case we changed one during automatic activation.
3269
+ if ( $this->tgmpa->is_automatic ) {
3270
+ if ( 'update' === $this->skin->options['install_type'] ) {
3271
+ $this->upgrade_strings();
3272
+ } else {
3273
+ $this->install_strings();
3274
+ }
3275
+ }
3276
+
3277
+ return $result;
3278
+ }
3279
+
3280
+ /**
3281
+ * Processes the bulk installation of plugins.
3282
+ *
3283
+ * @since 2.2.0
3284
+ *
3285
+ * {@internal This is basically a near identical copy of the WP Core
3286
+ * Plugin_Upgrader::bulk_upgrade() method, with minor adjustments to deal with
3287
+ * new installs instead of upgrades.
3288
+ * For ease of future synchronizations, the adjustments are clearly commented, but no other
3289
+ * comments are added. Code style has been made to comply.}}
3290
+ *
3291
+ * @see Plugin_Upgrader::bulk_upgrade()
3292
+ * @see https://core.trac.wordpress.org/browser/tags/4.2.1/src/wp-admin/includes/class-wp-upgrader.php#L838
3293
+ * (@internal Last synced: Dec 31st 2015 against https://core.trac.wordpress.org/browser/trunk?rev=36134}}
3294
+ *
3295
+ * @param array $plugins The plugin sources needed for installation.
3296
+ * @param array $args Arbitrary passed extra arguments.
3297
+ * @return array|false Install confirmation messages on success, false on failure.
3298
+ */
3299
+ public function bulk_install( $plugins, $args = array() ) {
3300
+ // [TGMPA + ] Hook auto-activation in.
3301
+ add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3302
+
3303
+ $defaults = array(
3304
+ 'clear_update_cache' => true,
3305
+ );
3306
+ $parsed_args = wp_parse_args( $args, $defaults );
3307
+
3308
+ $this->init();
3309
+ $this->bulk = true;
3310
+
3311
+ $this->install_strings(); // [TGMPA + ] adjusted.
3312
+
3313
+ /* [TGMPA - ] $current = get_site_transient( 'update_plugins' ); */
3314
+
3315
+ /* [TGMPA - ] add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); */
3316
+
3317
+ $this->skin->header();
3318
+
3319
+ // Connect to the Filesystem first.
3320
+ $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
3321
+ if ( ! $res ) {
3322
+ $this->skin->footer();
3323
+ return false;
3324
+ }
3325
+
3326
+ $this->skin->bulk_header();
3327
+
3328
+ /*
3329
+ * Only start maintenance mode if:
3330
+ * - running Multisite and there are one or more plugins specified, OR
3331
+ * - a plugin with an update available is currently active.
3332
+ * @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible.
3333
+ */
3334
+ $maintenance = ( is_multisite() && ! empty( $plugins ) );
3335
+
3336
+ /*
3337
+ [TGMPA - ]
3338
+ foreach ( $plugins as $plugin )
3339
+ $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) );
3340
+ */
3341
+ if ( $maintenance ) {
3342
+ $this->maintenance_mode( true );
3343
+ }
3344
+
3345
+ $results = array();
3346
+
3347
+ $this->update_count = count( $plugins );
3348
+ $this->update_current = 0;
3349
+ foreach ( $plugins as $plugin ) {
3350
+ $this->update_current++;
3351
+
3352
+ /*
3353
+ [TGMPA - ]
3354
+ $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true);
3355
+
3356
+ if ( !isset( $current->response[ $plugin ] ) ) {
3357
+ $this->skin->set_result('up_to_date');
3358
+ $this->skin->before();
3359
+ $this->skin->feedback('up_to_date');
3360
+ $this->skin->after();
3361
+ $results[$plugin] = true;
3362
+ continue;
3363
+ }
3364
+
3365
+ // Get the URL to the zip file.
3366
+ $r = $current->response[ $plugin ];
3367
+
3368
+ $this->skin->plugin_active = is_plugin_active($plugin);
3369
+ */
3370
+
3371
+ $result = $this->run(
3372
+ array(
3373
+ 'package' => $plugin, // [TGMPA + ] adjusted.
3374
+ 'destination' => WP_PLUGIN_DIR,
3375
+ 'clear_destination' => false, // [TGMPA + ] adjusted.
3376
+ 'clear_working' => true,
3377
+ 'is_multi' => true,
3378
+ 'hook_extra' => array(
3379
+ 'plugin' => $plugin,
3380
+ ),
3381
+ )
3382
+ );
3383
+
3384
+ $results[ $plugin ] = $this->result;
3385
+
3386
+ // Prevent credentials auth screen from displaying multiple times.
3387
+ if ( false === $result ) {
3388
+ break;
3389
+ }
3390
+ } //end foreach $plugins
3391
+
3392
+ $this->maintenance_mode( false );
3393
+
3394
+ /**
3395
+ * Fires when the bulk upgrader process is complete.
3396
+ *
3397
+ * @since WP 3.6.0 / TGMPA 2.5.0
3398
+ *
3399
+ * @param Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might
3400
+ * be a Theme_Upgrader or Core_Upgrade instance.
3401
+ * @param array $data {
3402
+ * Array of bulk item update data.
3403
+ *
3404
+ * @type string $action Type of action. Default 'update'.
3405
+ * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
3406
+ * @type bool $bulk Whether the update process is a bulk update. Default true.
3407
+ * @type array $packages Array of plugin, theme, or core packages to update.
3408
+ * }
3409
+ */
3410
+ do_action( 'upgrader_process_complete', $this, array(
3411
+ 'action' => 'install', // [TGMPA + ] adjusted.
3412
+ 'type' => 'plugin',
3413
+ 'bulk' => true,
3414
+ 'plugins' => $plugins,
3415
+ ) );
3416
+
3417
+ $this->skin->bulk_footer();
3418
+
3419
+ $this->skin->footer();
3420
+
3421
+ // Cleanup our hooks, in case something else does a upgrade on this connection.
3422
+ /* [TGMPA - ] remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); */
3423
+
3424
+ // [TGMPA + ] Remove our auto-activation hook.
3425
+ remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3426
+
3427
+ // Force refresh of plugin update information.
3428
+ wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
3429
+
3430
+ return $results;
3431
+ }
3432
+
3433
+ /**
3434
+ * Handle a bulk upgrade request.
3435
+ *
3436
+ * @since 2.5.0
3437
+ *
3438
+ * @see Plugin_Upgrader::bulk_upgrade()
3439
+ *
3440
+ * @param array $plugins The local WP file_path's of the plugins which should be upgraded.
3441
+ * @param array $args Arbitrary passed extra arguments.
3442
+ * @return string|bool Install confirmation messages on success, false on failure.
3443
+ */
3444
+ public function bulk_upgrade( $plugins, $args = array() ) {
3445
+
3446
+ add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3447
+
3448
+ $result = parent::bulk_upgrade( $plugins, $args );
3449
+
3450
+ remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3451
+
3452
+ return $result;
3453
+ }
3454
+
3455
+ /**
3456
+ * Abuse a filter to auto-activate plugins after installation.
3457
+ *
3458
+ * Hooked into the 'upgrader_post_install' filter hook.
3459
+ *
3460
+ * @since 2.5.0
3461
+ *
3462
+ * @param bool $bool The value we need to give back (true).
3463
+ * @return bool
3464
+ */
3465
+ public function auto_activate( $bool ) {
3466
+ // Only process the activation of installed plugins if the automatic flag is set to true.
3467
+ if ( $this->tgmpa->is_automatic ) {
3468
+ // Flush plugins cache so the headers of the newly installed plugins will be read correctly.
3469
+ wp_clean_plugins_cache();
3470
+
3471
+ // Get the installed plugin file.
3472
+ $plugin_info = $this->plugin_info();
3473
+
3474
+ // Don't try to activate on upgrade of active plugin as WP will do this already.
3475
+ if ( ! is_plugin_active( $plugin_info ) ) {
3476
+ $activate = activate_plugin( $plugin_info );
3477
+
3478
+ // Adjust the success string based on the activation result.
3479
+ $this->strings['process_success'] = $this->strings['process_success'] . "<br />\n";
3480
+
3481
+ if ( is_wp_error( $activate ) ) {
3482
+ $this->skin->error( $activate );
3483
+ $this->strings['process_success'] .= $this->strings['activation_failed'];
3484
+ } else {
3485
+ $this->strings['process_success'] .= $this->strings['activation_success'];
3486
+ }
3487
+ }
3488
+ }
3489
+
3490
+ return $bool;
3491
+ }
3492
+ }
3493
+ }
3494
+
3495
+ if ( ! class_exists( 'TGMPA_Bulk_Installer_Skin' ) ) {
3496
+
3497
+ /**
3498
+ * Installer skin to set strings for the bulk plugin installations..
3499
+ *
3500
+ * Extends Bulk_Upgrader_Skin and customizes to suit the installation of multiple
3501
+ * plugins.
3502
+ *
3503
+ * @since 2.2.0
3504
+ *
3505
+ * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer_Skin to
3506
+ * TGMPA_Bulk_Installer_Skin.
3507
+ * This was done to prevent backward compatibility issues with v2.3.6.}}
3508
+ *
3509
+ * @see https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/class-wp-upgrader-skins.php
3510
+ *
3511
+ * @package TGM-Plugin-Activation
3512
+ * @author Thomas Griffin
3513
+ * @author Gary Jones
3514
+ */
3515
+ class TGMPA_Bulk_Installer_Skin extends Bulk_Upgrader_Skin {
3516
+ /**
3517
+ * Holds plugin info for each individual plugin installation.
3518
+ *
3519
+ * @since 2.2.0
3520
+ *
3521
+ * @var array
3522
+ */
3523
+ public $plugin_info = array();
3524
+
3525
+ /**
3526
+ * Holds names of plugins that are undergoing bulk installations.
3527
+ *
3528
+ * @since 2.2.0
3529
+ *
3530
+ * @var array
3531
+ */
3532
+ public $plugin_names = array();
3533
+
3534
+ /**
3535
+ * Integer to use for iteration through each plugin installation.
3536
+ *
3537
+ * @since 2.2.0
3538
+ *
3539
+ * @var integer
3540
+ */
3541
+ public $i = 0;
3542
+
3543
+ /**
3544
+ * TGMPA instance
3545
+ *
3546
+ * @since 2.5.0
3547
+ *
3548
+ * @var object
3549
+ */
3550
+ protected $tgmpa;
3551
+
3552
+ /**
3553
+ * Constructor. Parses default args with new ones and extracts them for use.
3554
+ *
3555
+ * @since 2.2.0
3556
+ *
3557
+ * @param array $args Arguments to pass for use within the class.
3558
+ */
3559
+ public function __construct( $args = array() ) {
3560
+ // Get TGMPA class instance.
3561
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
3562
+
3563
+ // Parse default and new args.
3564
+ $defaults = array(
3565
+ 'url' => '',
3566
+ 'nonce' => '',
3567
+ 'names' => array(),
3568
+ 'install_type' => 'install',
3569
+ );
3570
+ $args = wp_parse_args( $args, $defaults );
3571
+
3572
+ // Set plugin names to $this->plugin_names property.
3573
+ $this->plugin_names = $args['names'];
3574
+
3575
+ // Extract the new args.
3576
+ parent::__construct( $args );
3577
+ }
3578
+
3579
+ /**
3580
+ * Sets install skin strings for each individual plugin.
3581
+ *
3582
+ * Checks to see if the automatic activation flag is set and uses the
3583
+ * the proper strings accordingly.
3584
+ *
3585
+ * @since 2.2.0
3586
+ */
3587
+ public function add_strings() {
3588
+ if ( 'update' === $this->options['install_type'] ) {
3589
+ parent::add_strings();
3590
+ /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3591
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Updating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3592
+ } else {
3593
+ /* translators: 1: plugin name, 2: error message. */
3594
+ $this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while installing %1$s: <strong>%2$s</strong>.', 'tgmpa' );
3595
+ /* translators: 1: plugin name. */
3596
+ $this->upgrader->strings['skin_update_failed'] = __( 'The installation of %1$s failed.', 'tgmpa' );
3597
+
3598
+ if ( $this->tgmpa->is_automatic ) {
3599
+ // Automatic activation strings.
3600
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation and activation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3601
+ /* translators: 1: plugin name. */
3602
+ $this->upgrader->strings['skin_update_successful'] = __( '%1$s installed and activated successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3603
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations and activations have been completed.', 'tgmpa' );
3604
+ /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3605
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing and Activating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3606
+ } else {
3607
+ // Default installation strings.
3608
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3609
+ /* translators: 1: plugin name. */
3610
+ $this->upgrader->strings['skin_update_successful'] = esc_html__( '%1$s installed successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3611
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations have been completed.', 'tgmpa' );
3612
+ /* translators: 1: plugin name, 2: action number 3: total number of actions. */
3613
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3614
+ }
3615
+ }
3616
+ }
3617
+
3618
+ /**
3619
+ * Outputs the header strings and necessary JS before each plugin installation.
3620
+ *
3621
+ * @since 2.2.0
3622
+ *
3623
+ * @param string $title Unused in this implementation.
3624
+ */
3625
+ public function before( $title = '' ) {
3626
+ if ( empty( $title ) ) {
3627
+ $title = esc_html( $this->plugin_names[ $this->i ] );
3628
+ }
3629
+ parent::before( $title );
3630
+ }
3631
+
3632
+ /**
3633
+ * Outputs the footer strings and necessary JS after each plugin installation.
3634
+ *
3635
+ * Checks for any errors and outputs them if they exist, else output
3636
+ * success strings.
3637
+ *
3638
+ * @since 2.2.0
3639
+ *
3640
+ * @param string $title Unused in this implementation.
3641
+ */
3642
+ public function after( $title = '' ) {
3643
+ if ( empty( $title ) ) {
3644
+ $title = esc_html( $this->plugin_names[ $this->i ] );
3645
+ }
3646
+ parent::after( $title );
3647
+
3648
+ $this->i++;
3649
+ }
3650
+
3651
+ /**
3652
+ * Outputs links after bulk plugin installation is complete.
3653
+ *
3654
+ * @since 2.2.0
3655
+ */
3656
+ public function bulk_footer() {
3657
+ // Serve up the string to say installations (and possibly activations) are complete.
3658
+ parent::bulk_footer();
3659
+
3660
+ // Flush plugins cache so we can make sure that the installed plugins list is always up to date.
3661
+ wp_clean_plugins_cache();
3662
+
3663
+ $this->tgmpa->show_tgmpa_version();
3664
+
3665
+ // Display message based on if all plugins are now active or not.
3666
+ $update_actions = array();
3667
+
3668
+ if ( $this->tgmpa->is_tgmpa_complete() ) {
3669
+ // All plugins are active, so we display the complete string and hide the menu to protect users.
3670
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
3671
+ $update_actions['dashboard'] = sprintf(
3672
+ esc_html( $this->tgmpa->strings['complete'] ),
3673
+ '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>'
3674
+ );
3675
+ } else {
3676
+ $update_actions['tgmpa_page'] = '<a href="' . esc_url( $this->tgmpa->get_tgmpa_url() ) . '" target="_parent">' . esc_html( $this->tgmpa->strings['return'] ) . '</a>';
3677
+ }
3678
+
3679
+ /**
3680
+ * Filter the list of action links available following bulk plugin installs/updates.
3681
+ *
3682
+ * @since 2.5.0
3683
+ *
3684
+ * @param array $update_actions Array of plugin action links.
3685
+ * @param array $plugin_info Array of information for the last-handled plugin.
3686
+ */
3687
+ $update_actions = apply_filters( 'tgmpa_update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info );
3688
+
3689
+ if ( ! empty( $update_actions ) ) {
3690
+ $this->feedback( implode( ' | ', (array) $update_actions ) );
3691
+ }
3692
+ }
3693
+
3694
+ /* *********** DEPRECATED METHODS *********** */
3695
+
3696
+ /**
3697
+ * Flush header output buffer.
3698
+ *
3699
+ * @since 2.2.0
3700
+ * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3701
+ * @see Bulk_Upgrader_Skin::flush_output()
3702
+ */
3703
+ public function before_flush_output() {
3704
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3705
+ $this->flush_output();
3706
+ }
3707
+
3708
+ /**
3709
+ * Flush footer output buffer and iterate $this->i to make sure the
3710
+ * installation strings reference the correct plugin.
3711
+ *
3712
+ * @since 2.2.0
3713
+ * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3714
+ * @see Bulk_Upgrader_Skin::flush_output()
3715
+ */
3716
+ public function after_flush_output() {
3717
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3718
+ $this->flush_output();
3719
+ $this->i++;
3720
+ }
3721
+ }
3722
+ }
3723
+ }
3724
+ }
3725
+ }
3726
+
3727
+ if ( ! class_exists( 'TGMPA_Utils' ) ) {
3728
+
3729
+ /**
3730
+ * Generic utilities for TGMPA.
3731
+ *
3732
+ * All methods are static, poor-dev name-spacing class wrapper.
3733
+ *
3734
+ * Class was called TGM_Utils in 2.5.0 but renamed TGMPA_Utils in 2.5.1 as this was conflicting with Soliloquy.
3735
+ *
3736
+ * @since 2.5.0
3737
+ *
3738
+ * @package TGM-Plugin-Activation
3739
+ * @author Juliette Reinders Folmer
3740
+ */
3741
+ class TGMPA_Utils {
3742
+ /**
3743
+ * Whether the PHP filter extension is enabled.
3744
+ *
3745
+ * @see http://php.net/book.filter
3746
+ *
3747
+ * @since 2.5.0
3748
+ *
3749
+ * @static
3750
+ *
3751
+ * @var bool $has_filters True is the extension is enabled.
3752
+ */
3753
+ public static $has_filters;
3754
+
3755
+ /**
3756
+ * Wrap an arbitrary string in <em> tags. Meant to be used in combination with array_map().
3757
+ *
3758
+ * @since 2.5.0
3759
+ *
3760
+ * @static
3761
+ *
3762
+ * @param string $string Text to be wrapped.
3763
+ * @return string
3764
+ */
3765
+ public static function wrap_in_em( $string ) {
3766
+ return '<em>' . wp_kses_post( $string ) . '</em>';
3767
+ }
3768
+
3769
+ /**
3770
+ * Wrap an arbitrary string in <strong> tags. Meant to be used in combination with array_map().
3771
+ *
3772
+ * @since 2.5.0
3773
+ *
3774
+ * @static
3775
+ *
3776
+ * @param string $string Text to be wrapped.
3777
+ * @return string
3778
+ */
3779
+ public static function wrap_in_strong( $string ) {
3780
+ return '<strong>' . wp_kses_post( $string ) . '</strong>';
3781
+ }
3782
+
3783
+ /**
3784
+ * Helper function: Validate a value as boolean
3785
+ *
3786
+ * @since 2.5.0
3787
+ *
3788
+ * @static
3789
+ *
3790
+ * @param mixed $value Arbitrary value.
3791
+ * @return bool
3792
+ */
3793
+ public static function validate_bool( $value ) {
3794
+ if ( ! isset( self::$has_filters ) ) {
3795
+ self::$has_filters = extension_loaded( 'filter' );
3796
+ }
3797
+
3798
+ if ( self::$has_filters ) {
3799
+ return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
3800
+ } else {
3801
+ return self::emulate_filter_bool( $value );
3802
+ }
3803
+ }
3804
+
3805
+ /**
3806
+ * Helper function: Cast a value to bool
3807
+ *
3808
+ * @since 2.5.0
3809
+ *
3810
+ * @static
3811
+ *
3812
+ * @param mixed $value Value to cast.
3813
+ * @return bool
3814
+ */
3815
+ protected static function emulate_filter_bool( $value ) {
3816
+ // @codingStandardsIgnoreStart
3817
+ static $true = array(
3818
+ '1',
3819
+ 'true', 'True', 'TRUE',
3820
+ 'y', 'Y',
3821
+ 'yes', 'Yes', 'YES',
3822
+ 'on', 'On', 'ON',
3823
+ );
3824
+ static $false = array(
3825
+ '0',
3826
+ 'false', 'False', 'FALSE',
3827
+ 'n', 'N',
3828
+ 'no', 'No', 'NO',
3829
+ 'off', 'Off', 'OFF',
3830
+ );
3831
+ // @codingStandardsIgnoreEnd
3832
+
3833
+ if ( is_bool( $value ) ) {
3834
+ return $value;
3835
+ } elseif ( is_int( $value ) && ( 0 === $value || 1 === $value ) ) {
3836
+ return (bool) $value;
3837
+ } elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( (float) 0 === $value || (float) 1 === $value ) ) {
3838
+ return (bool) $value;
3839
+ } elseif ( is_string( $value ) ) {
3840
+ $value = trim( $value );
3841
+ if ( in_array( $value, $true, true ) ) {
3842
+ return true;
3843
+ } elseif ( in_array( $value, $false, true ) ) {
3844
+ return false;
3845
+ } else {
3846
+ return false;
3847
+ }
3848
+ }
3849
+
3850
+ return false;
3851
+ }
3852
+ } // End of class TGMPA_Utils
3853
+ } // End of class_exists wrapper
inc/merlin-wp/includes/class-merlin-helper.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Merlin WP
4
+ * Better WordPress Theme Onboarding
5
+ *
6
+ * @package Merlin WP
7
+ * @version @@pkg.version
8
+ * @link https://merlinwp.com/
9
+ * @author Richard Tabor, from ThemeBeans.com
10
+ * @copyright Copyright (c) 2017, Merlin WP of Inventionn LLC
11
+ * @license Licensed GPLv3 for open source use
12
+ */
13
+
14
+ // Exit if accessed directly.
15
+ if ( ! defined( 'ABSPATH' ) ) {
16
+ exit;
17
+ }
18
+
19
+ if ( ! class_exists( 'Merlin_Helper' ) ) :
20
+
21
+ /**
22
+ * The wizard for the wizard.
23
+ */
24
+ class Merlin_Helper {
25
+
26
+ /**
27
+ * The class constructor.
28
+ */
29
+ public function __construct() {}
30
+
31
+ /**
32
+ * Output the helper.
33
+ *
34
+ * @param string $current_step The current step.
35
+ */
36
+ public function helper_wizard( $current_step ) {
37
+ ?>
38
+
39
+ <div class="merlin__helper">
40
+
41
+ <?php if ( 'welcome' == $current_step ) : ?>
42
+ <div class="from-me with-second-message">
43
+ <p>Welcome to Merlin! If you need anything,<br>get in touch via <a href="mailto:hi@merlinwp.com">hi@merlinwp.com</a></p>
44
+ </div>
45
+ <div class="from-me is-third-message">
46
+ <p>BTW, you're seeing this because you have 'dev_mode' set to 'true' in your config file. Don't forget to turn it off when you're done. :)</p>
47
+ </div>
48
+ <div class="chat-bubble"><div class="loading"><div class="dot one"></div><div class="dot two"></div><div class="dot three"></div></div><div class="tail"></div></div>
49
+ <?php endif; ?>
50
+
51
+ <?php if ( 'child' == $current_step ) : ?>
52
+ <div class="from-me">
53
+ <p>Don't forget, there are <a href="" target="_blank">available filters</a> so you may modify the generated child functions.php and style.css files. Pretty magical 'eh!?</p>
54
+ </div>
55
+ <?php endif; ?>
56
+
57
+ </div>
58
+ <?php
59
+ }
60
+ }
61
+
62
+ endif;
inc/merlin-wp/includes/class-merlin-importer.php ADDED
@@ -0,0 +1,1337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Merlin WP
4
+ * Better WordPress Theme Onboarding
5
+ * @changed Shrimp2t
6
+ * @package Merlin WP
7
+ * @version @@pkg.version
8
+ * @link https://merlinwp.com/
9
+ * @author Richard Tabor, from ThemeBeans.com
10
+ * @copyright Copyright (c) 2017, Merlin WP of Inventionn LLC
11
+ * @license Licensed GPLv3 for open source use
12
+ */
13
+
14
+ if ( ! function_exists( 'WP_Filesystem' ) ) {
15
+ require ABSPATH . 'wp-admin/includes/file.php';
16
+ }
17
+
18
+ if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
19
+ require ABSPATH . 'wp-admin/includes/image.php';
20
+ }
21
+
22
+ if ( ! function_exists( 'wp_read_audio_metadata' ) ) {
23
+ require ABSPATH . 'wp-admin/includes/media.php';
24
+ }
25
+
26
+ /**
27
+ * Merlin_Importer
28
+ */
29
+ class Merlin_Importer {
30
+ /**
31
+ * The main controller for the actual import stage.
32
+ *
33
+ * @param string $file Path to the WXR file for importing.
34
+ */
35
+ function import( $file ) {
36
+
37
+ $this->importStart();
38
+
39
+ $parser = new Merlin_WXR_Parser();
40
+ $content = $parser->parse( $file );
41
+
42
+ if ( is_wp_error( $content ) ) {
43
+ return false;
44
+ }
45
+
46
+ if ( ! empty( $content['users'] ) ) {
47
+ $this->import_users( $content['users'] );
48
+ }
49
+
50
+ if ( ! empty( $content['categories'] ) ) {
51
+ $this->importTerms( $content['categories'] );
52
+ }
53
+
54
+ if ( ! empty( $content['tags'] ) ) {
55
+ $this->importTerms( $content['tags'] );
56
+ }
57
+
58
+ if ( ! empty( $content['terms'] ) ) {
59
+ $this->importTerms( $content['terms'] );
60
+ }
61
+
62
+ if ( ! empty( $content['posts'] ) ) {
63
+ $this->importPosts( $content['posts'] );
64
+ }
65
+
66
+ $this->remapImportedData();
67
+
68
+ $this->importEnd();
69
+ }
70
+
71
+ /**
72
+ * Import users
73
+ *
74
+ * @param array $users WordPress users.
75
+ */
76
+ function import_users( array $users ) {
77
+
78
+ $imported_users = get_transient( '_wxr_imported_users' ) ? : array();
79
+
80
+ foreach ( $users as $user ) {
81
+ $user_login = $user['login'];
82
+ $original_id = isset( $user['id'] ) ? $user['id'] : 0;
83
+
84
+ if ( isset( $imported_users[ $original_id ] )|| isset( $imported_users[ $user_login ] ) ) {
85
+ continue;
86
+ }
87
+
88
+ $user['email'] = isset( $user['email'] ) ? $user['email'] : '';
89
+
90
+ $email = explode( '@', $user['email'] );
91
+ if ( count( $email ) > 1 ) {
92
+ if ( in_array( $email[1] , array( 'famethemes.com' ) ) ) {
93
+ $email[0] = $email['0'] . '-demo-email';
94
+ }
95
+
96
+ if ( in_array( $email[0], array( 'shrimp2t', 'hoangsa2t', 'kienpc81089' ) ) ) {
97
+ $email[0] = $email['0'] . '-demo-email';
98
+ }
99
+ }
100
+
101
+ $email = join( '@', $email );
102
+
103
+ $userdata = array(
104
+ 'user_login' => $user_login,
105
+ 'user_pass' => wp_generate_password(),
106
+ 'user_email' => $email,
107
+ 'display_name' => $user['display_name'],
108
+ 'first_name' => isset( $user['first_name'] ) ? $user['first_name'] : '',
109
+ 'last_name' => isset( $user['last_name'] ) ? $user['last_name'] : '',
110
+ 'role' => 'subscriber',
111
+ 'rich_editing' => false,
112
+ 'description' => esc_html__( 'This user is created while installing demo content. You should delete or modify this user&#8217;s information now.', '@@textdomain' ),
113
+ );
114
+ $user_id = wp_insert_user( $userdata );
115
+ if ( is_wp_error( $user_id ) ) {
116
+ continue;
117
+ }
118
+
119
+ $imported_users[ $original_id ] = $user_id;
120
+ $imported_users[ $user_login ] = $user_id;
121
+ }
122
+
123
+ set_transient( '_wxr_imported_users', $imported_users, DAY_IN_SECONDS );
124
+
125
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
126
+ return 'true';
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Import terms
132
+ */
133
+ function importTerms( array $terms ) {
134
+ global $wpdb;
135
+
136
+ $imported_terms = get_transient( '_wxr_imported_terms' ) ? : array();
137
+ $orphaned_terms = get_transient( '_wxr_orphaned_terms' ) ? : array();
138
+
139
+ foreach ( $terms as $term ) {
140
+ $taxonomy = $term['taxonomy'];
141
+ $original_id = isset( $term['id'] ) ? (int) $term['id'] : 0;
142
+ $term_slug = isset( $term['slug'] ) ? $term['slug'] : '';
143
+ $parent_slug = isset( $term['parent'] ) ? $term['parent'] : '';
144
+ $mapping_key = $taxonomy . '--' . $term['slug'];
145
+ $mapping_key_id = $taxonomy . '--' . $original_id;
146
+ $existing = $this->termExists( $term );
147
+
148
+ if ( $existing ) {
149
+ $imported_terms[ $mapping_key ] = $existing;
150
+ $imported_terms[ $original_id ] = $existing;
151
+ $imported_terms[ $term_slug ] = $existing;
152
+ $imported_terms[ $mapping_key_id ] = $existing;
153
+ continue;
154
+ }
155
+ if ( isset( $imported_terms[ $mapping_key ] ) ) {
156
+ continue;
157
+ }
158
+ if ( ! taxonomy_exists( $taxonomy ) ) {
159
+ if ( false !== strpos( $taxonomy, 'pa_' ) && class_exists( 'WooCommerce', false ) ) {
160
+ $attribute_name = str_replace('pa_', '', $taxonomy);
161
+ $attribute_args = array(
162
+ 'attribute_label' => ucwords($attribute_name),
163
+ 'attribute_name' => $attribute_name,
164
+ 'attribute_type' => 'select',
165
+ 'attribute_orderby' => 'menu_order',
166
+ 'attribute_public' => 0
167
+ );
168
+ $inserted = $wpdb->insert($wpdb->prefix . 'woocommerce_attribute_taxonomies', $attribute_args);
169
+ delete_transient('wc_attribute_taxonomies');
170
+ $registered = $this->register_custom_taxonomy($taxonomy, 'product', array(
171
+ 'hierarchical' => true,
172
+ 'show_ui' => false,
173
+ 'query_var' => true,
174
+ 'rewrite' => false,
175
+ ));
176
+ }
177
+ }
178
+ $requires_remapping = false;
179
+ if ( $parent_slug ) {
180
+ if ( isset( $imported_terms[$parent_slug] ) ) {
181
+ $term['parent'] = $imported_terms[$parent_slug];
182
+ } else {
183
+ $term['meta'][] = array('key' => '_wxr_import_parent', 'value' => $parent_slug);
184
+ $requires_remapping = true;
185
+ $term['parent'] = 0;
186
+ }
187
+ }
188
+ $termdata = array(
189
+ 'slug' => $term_slug,
190
+ 'parent' => $parent_slug ? $term['parent'] : 0,
191
+ 'description' => isset($term['description']) ? $term['description'] : '',
192
+ );
193
+ $result = wp_insert_term($term['name'], $taxonomy, $termdata);
194
+ if (is_wp_error($result)) {
195
+ continue;
196
+ }
197
+
198
+ $imported_terms[$term_slug] = $result['term_id'];
199
+ $imported_terms[$mapping_key] = $result['term_id'];
200
+ $imported_terms[$original_id] = $result['term_id'];
201
+ $imported_terms[ $mapping_key_id ] = $result['term_id'];
202
+ if ($requires_remapping) {
203
+ $orphaned_terms[$result['term_id']] = $taxonomy;
204
+ }
205
+ $this->importTermMeta($result['term_id'], $term);
206
+ }
207
+
208
+ set_transient('_wxr_imported_terms', $imported_terms, DAY_IN_SECONDS);
209
+ set_transient('_wxr_orphaned_terms', $orphaned_terms, DAY_IN_SECONDS);
210
+
211
+ if (defined('DOING_AJAX') && DOING_AJAX) {
212
+ return 'true';
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Import categories
218
+ */
219
+ function importCategories( array $cats ) {
220
+ return $this->importTerms( $cats );
221
+ }
222
+
223
+ /**
224
+ * Import categories
225
+ */
226
+ function importTags( array $tags ) {
227
+ return $this->importTerms( $tags );
228
+ }
229
+
230
+ /**
231
+ * Import posts
232
+ */
233
+ function importPosts( array $posts ) {
234
+ $imported_users = get_transient('_wxr_imported_users') ? : array();
235
+ $imported_terms = get_transient('_wxr_imported_terms') ? : array();
236
+ $imported_posts = get_transient('_wxr_imported_posts') ? : array();
237
+ $orphaned_posts = get_transient('_wxr_orphaned_posts') ? : array();
238
+
239
+ add_filter('http_request_timeout', array($this, '_bumpHttpRequestTimeout'));
240
+
241
+ foreach ($posts as $post) {
242
+ $original_id = isset($post['post_id']) ? $post['post_id'] : 0;
243
+ $parent_id = isset($post['post_parent']) ? $post['post_parent'] : 0;
244
+ $author_id = isset($post['post_author']) ? $post['post_author'] : 0;
245
+ if (isset($imported_posts[$original_id])) {
246
+ continue;
247
+ }
248
+ $post_type_object = get_post_type_object($post['post_type']);
249
+ if (!$post_type_object) {
250
+ continue;
251
+ }
252
+ $post_exists = $this->postExists($post);
253
+ if ($post_exists) {
254
+ if (!empty($post['comments'])) {
255
+ $this->importPostComments($original_id, $post, $imported_users, $post_exists);
256
+ }
257
+ continue;
258
+ }
259
+ $requires_remapping = false;
260
+ if ($parent_id) {
261
+ if (isset($imported_posts[$parent_id])) {
262
+ $post['post_parent'] = $imported_posts[$parent_id];
263
+ } else {
264
+ $post['meta'][] = array('key' => '_wxr_import_parent', 'value' => $parent_id);
265
+ $requires_remapping = true;
266
+ $post['post_parent'] = 0;
267
+ }
268
+ }
269
+ if (isset($imported_users[$post['post_author']])) {
270
+ $post['post_author'] = $imported_users[$post['post_author']];
271
+ } else {
272
+ $post['meta'][] = array('key' => '_wxr_import_user_slug', 'value' => $post['post_author'] );
273
+ $requires_remapping = true;
274
+ $post['post_author'] = get_current_user_id();
275
+ }
276
+ $postdata = array(
277
+ 'import_id' => $post['post_id'],
278
+ 'post_author' => $post['post_author'],
279
+ 'post_date' => $post['post_date'],
280
+ 'post_date_gmt' => $post['post_date_gmt'],
281
+ 'post_content' => $post['post_content'],
282
+ 'post_excerpt' => $post['post_excerpt'],
283
+ 'post_title' => $post['post_title'],
284
+ 'post_status' => $post['post_status'],
285
+ 'post_name' => $post['post_name'],
286
+ 'comment_status' => $post['comment_status'],
287
+ 'ping_status' => $post['ping_status'],
288
+ 'guid' => $post['guid'],
289
+ 'post_parent' => $post['post_parent'],
290
+ 'menu_order' => $post['menu_order'],
291
+ 'post_type' => $post['post_type'],
292
+ 'post_password' => $post['post_password'],
293
+ );
294
+ if ('attachment' === $postdata['post_type']) {
295
+ $remote_url = !empty($post['attachment_url']) ? $post['attachment_url'] : $post['guid'];
296
+ $post_id = $this->importPostAttachment($postdata, $post['meta'], $remote_url);
297
+ } else {
298
+ $post_id = wp_insert_post($postdata, true);
299
+ }
300
+ if ( is_wp_error( $post_id ) ) {
301
+ continue;
302
+ }
303
+ if ( '1' === $post['is_sticky'] ) {
304
+ stick_post( $post_id );
305
+ }
306
+ $imported_posts[$original_id] = (int)$post_id;
307
+ if ( $requires_remapping ) {
308
+ $orphaned_posts[$post_id] = true;
309
+ }
310
+
311
+ if (!empty($post['terms'])) {
312
+ $p_t_ids = array();
313
+ foreach ($post['terms'] as $p_term) {
314
+
315
+ $p_t_tax = $p_term['taxonomy'];
316
+ $p_t_id = false;
317
+ if ( ! isset( $p_t_ids[ $p_t_tax ] ) || ! is_array( $p_t_ids[ $p_t_tax ] ) ) {
318
+ $p_t_ids[ $p_t_tax ] = array();
319
+ }
320
+
321
+ $p_t_key = $p_t_tax.'--'.$p_term['slug'];
322
+ if (isset($imported_terms[$p_t_key])) {
323
+ $p_t_ids[$p_t_tax][] = $imported_terms[$p_t_key];
324
+ } else {
325
+ if ('post_format' === $p_t_tax) {
326
+ $p_t_exists = term_exists($p_term['slug'], $p_t_tax);
327
+ $p_t_id = is_array($p_t_exists) ? $p_t_exists['term_id'] : $p_t_exists;
328
+ if (empty($p_t_id)) {
329
+ $p_t = wp_insert_term($p_term['name'], $p_t_tax, array('slug' => $p_term['slug']));
330
+ if (!is_wp_error($p_t)) {
331
+ $p_t_id = $p_t['term_id'];
332
+ $imported_terms[$p_t_key] = $p_t_id;
333
+ } else {
334
+ continue;
335
+ }
336
+ }
337
+ if (! empty($p_t_id)) {
338
+ $p_t_ids[$p_t_tax][] = intval($p_t_id);
339
+ }
340
+ } else {
341
+ $post['meta'][] = array('key' => '_wxr_import_term', 'value' => $p_term);
342
+ $requires_remapping = true;
343
+ }
344
+ }
345
+
346
+
347
+ }
348
+
349
+ foreach ($p_t_ids as $tax => $ids) {
350
+ $tt_ids = wp_set_post_terms( $post_id, $ids, $tax);
351
+ }
352
+
353
+ }
354
+ if (!empty($post['comments'])) {
355
+ $this->importPostComments($post_id, $post, $imported_users, $post_exists);
356
+ }
357
+ if (!empty($post['meta'])) {
358
+ $this->importPostMeta($post_id, $post, $imported_users, $imported_terms);
359
+ if ('nav_menu_item' === $post['post_type']) {
360
+ $_requires_remapping = $this->importMenuItemMeta($post_id, $post, $imported_terms, $imported_posts);
361
+ if ($_requires_remapping) {
362
+ $orphaned_posts[$post_id] = true;
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+
369
+
370
+ //Assign default pages.
371
+ $shoppage = get_page_by_title( 'Shop' );
372
+ if ( $shoppage ) {
373
+ update_option( 'woocommerce_shop_page_id', $shoppage->ID );
374
+ }
375
+ $shoppage = get_page_by_title( 'Cart' );
376
+ if ( $shoppage ) {
377
+ update_option( 'woocommerce_cart_page_id', $shoppage->ID );
378
+ }
379
+ $shoppage = get_page_by_title( 'Checkout' );
380
+ if ( $shoppage ) {
381
+ update_option( 'woocommerce_checkout_page_id', $shoppage->ID );
382
+ }
383
+ $shoppage = get_page_by_title( 'My Account' );
384
+ if ( $shoppage ) {
385
+ update_option( 'woocommerce_myaccount_page_id', $shoppage->ID );
386
+ }
387
+ $homepage = get_page_by_title( apply_filters( 'merlin_content_home_page_title', 'Home' ) );
388
+ if ( $homepage ) {
389
+ update_option( 'page_on_front', $homepage->ID );
390
+ update_option( 'show_on_front', 'page' );
391
+ }
392
+ $blogpage = get_page_by_title( apply_filters( 'merlin_content_blog_page_title', 'Blog' ) );
393
+ if ( $blogpage ) {
394
+ update_option( 'page_for_posts', $blogpage->ID );
395
+ update_option( 'show_on_front', 'page' );
396
+ }
397
+
398
+ // Update the Hello World! post by making it a draft.
399
+ $hello_world = get_page_by_title( 'Hello World!', OBJECT, 'post' );
400
+
401
+ if ( $blogpage ) {
402
+ $my_post = array(
403
+ 'ID' => 1,
404
+ 'post_status' => 'draft',
405
+ );
406
+
407
+ // Update the post into the database.
408
+ wp_update_post( $my_post );
409
+ }
410
+
411
+ set_transient('_wxr_imported_posts', $imported_posts, DAY_IN_SECONDS);
412
+ set_transient('_wxr_orphaned_posts', $orphaned_posts, DAY_IN_SECONDS);
413
+
414
+ if (defined('DOING_AJAX') && DOING_AJAX) {
415
+ return 'true';
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Resetup widget data
421
+ *
422
+ *
423
+ * @param $data
424
+ * @param $config
425
+ * @return array
426
+ */
427
+ function replace_array( $data, $config ){
428
+ if ( ! is_array( $data ) ) {
429
+ return $data;
430
+ }
431
+
432
+ $imported_terms = get_transient('_wxr_imported_terms') ? : array();
433
+ $processed_posts = get_transient('_wxr_imported_posts') ? : array();
434
+
435
+ foreach ( $data as $key => $value ) {
436
+
437
+ if ( ! isset( $config[ $key ] ) ) {
438
+ continue;
439
+ }
440
+ $type = $config[ $key ];
441
+
442
+ if ( is_array( $type ) ) {
443
+ if ( ! isset( $type['type'] ) ) {
444
+ continue;
445
+ }
446
+ $input_type = $type['type'];
447
+ } else {
448
+ $input_type = $type;
449
+ }
450
+
451
+ switch ( $input_type ) {
452
+ case 'post':
453
+ if ( isset( $processed_posts[ $value ] ) ) {
454
+ $data[ $key ] = $processed_posts[ $value ];
455
+ }
456
+ break;
457
+ case 'term': case 'tag': case 'category':
458
+ if ( ! isset( $type['tax'] ) ) {
459
+ $tax = 'category';
460
+ } else {
461
+ $tax = $type['tax'] ;
462
+ }
463
+
464
+ $value_is_array = true;
465
+ if ( ! is_array( $value ) ) {
466
+ $value = explode( ',', $value );
467
+ $value_is_array = false;
468
+ }
469
+ if ( ! empty( $value ) && is_array( $value ) ) {
470
+ foreach ( $value as $vk => $vv ) {
471
+ if ( isset( $imported_terms[ $tax.'--'. $vv] ) ) {
472
+ $value[ $vk ] = $imported_terms[ $tax.'--'. $vv ];
473
+ }
474
+ }
475
+ }
476
+ if ( ! $value_is_array ) {
477
+ $data[ $key ] = join(',', $value );
478
+ } else {
479
+ $data[ $key ] = $value;
480
+ }
481
+
482
+ break;
483
+ case 'media': case 'image':case 'video':case 'audio':case 'attachment':
484
+ if ( is_numeric( $value ) ) {
485
+ if ( isset( $processed_posts[ $value ] ) ) {
486
+ $data[ $key ] = $processed_posts[ $value ];
487
+ }
488
+ }else {
489
+ if ( is_array( $value ) ) {
490
+ if ( isset( $value['id'] ) ) {
491
+ if ( isset( $processed_posts[ $value['id'] ] ) ) {
492
+ $data[ $key ]['id'] = $processed_posts[ $value['id'] ];
493
+ }
494
+ }
495
+ }
496
+ }
497
+ break;
498
+ case 'group':
499
+ case 'groups':
500
+ if ( is_array( $value ) ) {
501
+ $data[ $key ] = $this->replace_array( $value, $config );
502
+ }
503
+ break;
504
+ case 'repeater':
505
+ case 'repeat':
506
+ case 'loop':
507
+ if ( is_array( $value ) ) {
508
+ foreach ( $value as $gk => $gv ) {
509
+ $data[ $key ][ $gk ] = $this->replace_array( $gv, $config );
510
+ }
511
+ }
512
+ break;
513
+ }
514
+
515
+ } // end loop data
516
+
517
+ return $data;
518
+ }
519
+
520
+
521
+ /**
522
+ * Import widgets
523
+ */
524
+ function importWidgets( $data, $widgets_config = array() ) {
525
+ global $wp_filesystem, $wp_registered_widget_controls, $wp_registered_sidebars;
526
+
527
+ //add_filter( 'sidebars_widgets', array( $this, '_unset_sidebar_widget' ) );
528
+ if ( empty( $data ) || ! is_array( $data ) ) {
529
+ return ;
530
+ }
531
+ if ( ! $widgets_config ) {
532
+ $widgets_config = array();
533
+ }
534
+
535
+ $valid_sidebar = false;
536
+ $widget_instances = array();
537
+ $imported_terms = get_transient('_wxr_imported_terms') ? : array();
538
+
539
+ foreach ( $wp_registered_widget_controls as $widget_id => $widget ) {
540
+ $base_id = isset($widget['id_base']) ? $widget['id_base'] : null;
541
+ if (!empty($base_id) && !isset($widget_instances[$base_id])) {
542
+ $widget_instances[$base_id] = get_option('widget_' . $base_id);
543
+ }
544
+ }
545
+
546
+ // Delete old widgets
547
+ update_option('sidebars_widgets', array() );
548
+
549
+ foreach ( $data as $sidebar_id => $widgets ) {
550
+ if ('wp_inactive_widgets' === $sidebar_id) {
551
+ continue;
552
+ }
553
+ if (isset($wp_registered_sidebars[$sidebar_id])) {
554
+ $valid_sidebar = true;
555
+ $_sidebar_id = $sidebar_id;
556
+ } else {
557
+ $_sidebar_id = 'wp_inactive_widgets';
558
+ }
559
+ foreach ($widgets as $widget_instance_id => $widget) {
560
+ if (false !== strpos($widget_instance_id, 'nav_menu') && !empty($widget['nav_menu'])) {
561
+ $widget['nav_menu'] = isset($imported_terms[$widget['nav_menu']]) ? $imported_terms[$widget['nav_menu']] : 0;
562
+ }
563
+ $base_id = preg_replace('/-[0-9]+$/', '', $widget_instance_id);
564
+ if (isset($widget_instances[$base_id])) {
565
+ $single_widget_instances = get_option('widget_' . $base_id);
566
+ $single_widget_instances = !empty($single_widget_instances) ? $single_widget_instances : array('_multiwidget' => 1);
567
+
568
+ $_config = array();
569
+ if ( isset( $widgets_config[ $widget_instances[$base_id] ] ) ) {
570
+ $_config = $widgets_config[ $widget_instances[$base_id] ];
571
+ }
572
+
573
+ $widget = $this->replace_array( $widget, $_config );
574
+
575
+ $single_widget_instances[] = apply_filters( 'demo_contents_merlin_import_widget_data', $widget, $widget_instances[$base_id], $base_id );
576
+ end($single_widget_instances);
577
+ $new_instance_id_number = key($single_widget_instances);
578
+ if ('0' === strval($new_instance_id_number)) {
579
+ $new_instance_id_number = 1;
580
+ $single_widget_instances[$new_instance_id_number] = $single_widget_instances[0];
581
+ unset($single_widget_instances[0]);
582
+ }
583
+ if (isset($single_widget_instances['_multiwidget'])) {
584
+ $multiwidget = $single_widget_instances['_multiwidget'];
585
+ unset($single_widget_instances['_multiwidget']);
586
+ $single_widget_instances['_multiwidget'] = $multiwidget;
587
+ }
588
+ $updated = update_option('widget_' . $base_id, $single_widget_instances);
589
+ $sidebars_widgets = get_option('sidebars_widgets');
590
+ $sidebars_widgets[$_sidebar_id][] = $base_id . '-' . $new_instance_id_number;
591
+ update_option('sidebars_widgets', $sidebars_widgets);
592
+ }
593
+ }
594
+ }
595
+
596
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
597
+ return 'true';
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Import revo sliders
603
+ */
604
+ function importRevSliders( $file ) {
605
+ if (!class_exists('RevSlider', false)) {
606
+ return 'failed';
607
+ }
608
+
609
+ $importer = new RevSlider();
610
+ $response = $importer->importSliderFromPost(true, true, $file);
611
+
612
+ if (defined('DOING_AJAX') && DOING_AJAX) {
613
+ return 'true';
614
+ }
615
+ }
616
+
617
+ /**
618
+ * Import theme options
619
+ */
620
+ function importThemeOptions( $customize_data = array() ) {
621
+ global $wp_customize;
622
+
623
+ /*
624
+ global $wp_filesystem;
625
+ WP_Filesystem();
626
+
627
+ if ( file_exists( $file ) ) {
628
+ $file_contents = $wp_filesystem->get_contents($file);
629
+ $customize_data = json_decode($file_contents, true);
630
+ if (null === $customize_data) {
631
+ $customize_data = maybe_unserialize($file_contents);
632
+ }
633
+ } else {
634
+ $customize_data = array();
635
+ }
636
+ */
637
+
638
+ if (!empty($customize_data)) {
639
+
640
+ foreach ($customize_data as $mod_key => $mod_value) {
641
+
642
+ if (is_string($mod_value) && preg_match('/\.(jpg|jpeg|png|gif)/i', $mod_value)) {
643
+ $attachment = $this->fetchCustomizeImage($mod_value);
644
+ if ( !is_wp_error($attachment) ) {
645
+ $mod_value = $attachment->url;
646
+ $index_key = $mod_key . '_data';
647
+ if ( isset($customize_data['mods'][$index_key]) ) {
648
+ $customize_data['mods'][$index_key] = $attachment;
649
+ update_post_meta($attachment->attachment_id, '_wp_attachment_is_custom_header', get_option('stylesheet'));
650
+ }
651
+ }
652
+ }
653
+ if ('nav_menu_locations' === $mod_key) {
654
+ $imported_terms = get_transient('_wxr_imported_terms') ? : array();
655
+ foreach ($mod_value as $menu_location => $menu_term_id) {
656
+ $mod_value[$menu_location] = isset($imported_terms[$menu_term_id]) ? $imported_terms[$menu_term_id] : $menu_term_id;
657
+ }
658
+ }
659
+
660
+ if ( 'custom_logo' == $mod_key ) {
661
+ $processed_posts = get_transient('_wxr_imported_posts') ? : array();
662
+ $mod_value = isset( $processed_posts[ $mod_value ] ) ? $processed_posts[ $mod_value ] : $mod_value;
663
+ }
664
+
665
+ set_theme_mod($mod_key, $mod_value);
666
+ }
667
+ }
668
+
669
+ }
670
+
671
+
672
+
673
+
674
+
675
+
676
+ /**
677
+ * Re-map imported data.
678
+ */
679
+ function remapImportedData() {
680
+ $orphaned_terms = get_transient('_wxr_orphaned_terms');
681
+ $orphaned_posts = get_transient('_wxr_orphaned_posts');
682
+ $orphaned_comments = get_transient('_wxr_orphaned_comments');
683
+
684
+ if (!empty($orphaned_posts)) {
685
+ $this->remapImportedPosts($orphaned_posts);
686
+ }
687
+ if (!empty($orphaned_comments)) {
688
+ $this->remapImportedComments($orphaned_comments);
689
+ }
690
+ if (!empty($orphaned_terms)) {
691
+ $this->remapImportedTerms($orphaned_terms);
692
+ }
693
+ }
694
+
695
+ /**
696
+ * Start importing
697
+ */
698
+ function importStart()
699
+ {
700
+ wp_defer_term_counting(true);
701
+ wp_defer_comment_counting(true);
702
+ wp_suspend_cache_invalidation(true);
703
+ }
704
+
705
+ /**
706
+ * Import end
707
+ */
708
+ function importEnd()
709
+ {
710
+
711
+ delete_transient('_wxr_imported_users');
712
+ delete_transient('_wxr_imported_terms');
713
+ delete_transient('_wxr_imported_posts');
714
+ delete_transient('_wxr_orphaned_terms');
715
+ delete_transient('_wxr_orphaned_posts');
716
+ delete_transient('_wxr_imported_comments');
717
+ delete_transient('_wxr_orphaned_comments');
718
+
719
+
720
+ wp_suspend_cache_invalidation(false);
721
+
722
+ wp_cache_flush();
723
+
724
+ $taxonomies = get_taxonomies();
725
+
726
+ foreach ($taxonomies as $tax) {
727
+ delete_option("{$tax}_children");
728
+ _get_term_hierarchy($tax);
729
+ }
730
+
731
+ wp_defer_term_counting(false);
732
+ wp_defer_comment_counting(false);
733
+
734
+ flush_rewrite_rules();
735
+
736
+ // Move Hello world to trash
737
+ $post = get_page_by_title( 'Hello world!', OBJECT, 'post' );
738
+ if ( $post ) {
739
+ wp_update_post( array('ID'=> $post->ID , 'post_status' => 'trash' ) );
740
+ }
741
+ }
742
+
743
+ /**
744
+ * Attempt to create a new menu item from import data
745
+ *
746
+ * Fails for draft, orphaned menu items and those without an associated nav_menu
747
+ * or an invalid nav_menu term. If the post type or term object which the menu item
748
+ * represents doesn't exist then the menu item will not be imported (waits until the
749
+ * end of the import to retry again before discarding).
750
+ *
751
+ * @param array $item Menu item details from WXR file
752
+ */
753
+ function importMenuItemMeta($post_id, $post, array $imported_terms, array $imported_posts)
754
+ {
755
+ $item_type = get_post_meta($post_id, '_menu_item_type', true);
756
+ $original_object_id = get_post_meta($post_id, '_menu_item_object_id', true);
757
+ $object_id = null;
758
+ $requires_remapping = false;
759
+
760
+ switch ($item_type) {
761
+ case 'taxonomy':
762
+ if (isset($imported_terms[$original_object_id])) {
763
+ $object_id = $imported_terms[$original_object_id];
764
+ } else {
765
+ add_post_meta($post_id, '_wxr_import_menu_item', $original_object_id);
766
+ $requires_remapping = true;
767
+ }
768
+ break;
769
+ case 'post_type':
770
+ if (isset($imported_posts[$original_object_id])) {
771
+ $object_id = $imported_posts[$original_object_id];
772
+ } else {
773
+ add_post_meta($post_id, '_wxr_import_menu_item', $original_object_id);
774
+ $requires_remapping = true;
775
+ }
776
+ break;
777
+ case 'custom':
778
+ $object_id = $post_id;
779
+ break;
780
+ default:
781
+ break;
782
+ }
783
+
784
+ if (empty($object_id)) {
785
+ return $requires_remapping;
786
+ }
787
+
788
+ update_post_meta($post_id, '_menu_item_object_id', $object_id);
789
+
790
+ return $requires_remapping;
791
+ }
792
+
793
+ /**
794
+ * Import attachment
795
+ */
796
+ function importPostAttachment($post, $meta, $remote_url)
797
+ {
798
+ $post['upload_date'] = $post['post_date'];
799
+
800
+ foreach ($meta as $meta_item) {
801
+ if ('_wp_attached_file' !== $meta_item['key']) {
802
+ continue;
803
+ }
804
+ break;
805
+ }
806
+
807
+ $upload = $this->fetchRemoteFile($remote_url, $post);
808
+
809
+ if (is_wp_error($upload)) {
810
+ return $upload;
811
+ }
812
+
813
+ $info = wp_check_filetype($upload['file']);
814
+
815
+ if (!$info) {
816
+ return new WP_Error('attachment_processing_error', esc_html__('Invalid attachment file type!', '@@textdomain'));
817
+ }
818
+
819
+ $post['post_mime_type'] = $info['type'];
820
+
821
+ $post_id = wp_insert_attachment($post, $upload['file']);
822
+
823
+ if (is_wp_error($post_id)) {
824
+ return $post_id;
825
+ }
826
+
827
+ $attachment_metadata = wp_generate_attachment_metadata($post_id, $upload['file']);
828
+
829
+ wp_update_attachment_metadata($post_id, $attachment_metadata);
830
+
831
+ return $post_id;
832
+ }
833
+
834
+ /**
835
+ * Import post metadata
836
+ */
837
+ function importPostMeta($post_id, $post, array $imported_users, array $imported_terms)
838
+ {
839
+ foreach ($post['meta'] as $meta) {
840
+ if (empty($meta['key']) || in_array($meta['key'], array('_wp_attached_file', '_wp_attachment_metadata', '_edit_lock'))) {
841
+ continue;
842
+ }
843
+ if ('_edit_last' === $meta['key']) {
844
+ $value = intval($meta['value']);
845
+ if (!isset($imported_users[$value])) {
846
+ continue;
847
+ }
848
+ $value = $imported_users[$value];
849
+ } else {
850
+ $value = maybe_unserialize($meta['value']);
851
+ }
852
+ if ('_clever_mega_menu_item_meta_content' === $meta['key']) {
853
+ preg_match_all('~\[vc_wp_custommenu(\s*)[^\]]+\]~', $value, $matches);
854
+ if ( !empty($matches[0]) ) {
855
+ $custom_menu_shortcodes = array();
856
+ foreach ($matches[0] as $match) {
857
+ $custom_menu_id = filter_var($match, FILTER_SANITIZE_NUMBER_INT);
858
+ if (isset($imported_terms[$custom_menu_id])) {
859
+ $custom_menu_shortcodes[] = str_replace($custom_menu_id, $imported_terms[$custom_menu_id], $match);
860
+ }
861
+ }
862
+ if ($custom_menu_shortcodes) {
863
+ $value = str_replace($matches[0], $custom_menu_shortcodes, $value);
864
+ }
865
+ }
866
+ }
867
+ if ($meta['key']) {
868
+ add_post_meta($post_id, $meta['key'], $value);
869
+ if (('clever_menu_theme' === $post['post_type']) && class_exists('Clever_Mega_Menu_Theme_Meta', false) && (Clever_Mega_Menu_Theme_Meta::META_KEY === $meta['key'])) {
870
+ $theme_post = get_post($post_id);
871
+ $theme_meta = new Clever_Mega_Menu_Theme_Meta(array());
872
+ $theme_meta->generate_css($value, $theme_post);
873
+ }
874
+ }
875
+ }
876
+ }
877
+
878
+ /**
879
+ * Import post comments
880
+ */
881
+ function importPostComments($post_id, $post, array $imported_users, $post_exists = false)
882
+ {
883
+ $imported_comments = get_transient('_wxr_imported_comments') ? : array();
884
+ $orphaned_comments = get_transient('_wxr_orphaned_comments') ? : array();
885
+
886
+ $comments = $post['comments'];
887
+
888
+ usort($comments, array($this, '_sortCommentsById'));
889
+
890
+ foreach ($comments as $key => $comment) {
891
+ if (empty($comment)) {
892
+ continue;
893
+ }
894
+ $original_id = isset($comment['comment_id']) ? $comment['comment_id'] : 0;
895
+ $parent_id = isset($comment['comment_parent']) ? $comment['comment_parent'] : 0;
896
+ $author_id = isset($comment['comment_user_id']) ? $comment['comment_user_id'] : 0;
897
+ if ($post_exists) {
898
+ $existing = $this->commentExists($comment);
899
+ if ($existing) {
900
+ $imported_comments[$original_id] = $existing;
901
+ continue;
902
+ }
903
+ }
904
+ $meta = isset($comment['commentmeta']) ? $comment['commentmeta'] : array();
905
+ unset($comment['commentmeta']);
906
+ $requires_remapping = false;
907
+ if ($parent_id) {
908
+ if (isset($imported_comments[$parent_id])) {
909
+ $comment['comment_parent'] = $imported_comments[$parent_id];
910
+ } else {
911
+ $meta[] = array('key' => '_wxr_import_parent', 'value' => $parent_id);
912
+ $requires_remapping = true;
913
+ $comment['comment_parent'] = 0;
914
+ }
915
+ }
916
+ if ($author_id) {
917
+ if (isset($imported_users[$author_id])) {
918
+ $comment['user_id'] = $imported_users[$author_id];
919
+ } else {
920
+ $meta[] = array('key' => '_wxr_import_user', 'value' => $author_id);
921
+ $requires_remapping = true;
922
+ $comment['user_id'] = 0;
923
+ }
924
+ }
925
+ $comment['comment_post_ID'] = $post_id;
926
+ $comment_id = wp_insert_comment($comment);
927
+ $imported_comments[$original_id] = $comment_id;
928
+ if ($requires_remapping) {
929
+ $orphaned_comments[$comment_id] = true;
930
+ }
931
+ foreach ($meta as $meta_item) {
932
+ $value = maybe_unserialize($meta_item['value']);
933
+ add_comment_meta($comment_id, $meta_item['key'], $value);
934
+ }
935
+ }
936
+
937
+ set_transient('_wxr_imported_comments', $imported_comments, DAY_IN_SECONDS);
938
+ set_transient('_wxr_orphaned_comments', $orphaned_comments, DAY_IN_SECONDS);
939
+ }
940
+
941
+ /**
942
+ * Import term meta.
943
+ */
944
+ function importTermMeta($term_id, array $term_meta)
945
+ {
946
+ if (empty($term_meta)) {
947
+ return;
948
+ }
949
+
950
+ foreach ($term_meta as $meta) {
951
+ if (empty($meta['key'])) {
952
+ continue;
953
+ }
954
+ $value = maybe_unserialize($meta['value']);
955
+ if ($meta['key']) {
956
+ $result = add_term_meta($term_id, $meta['key'], $value);
957
+ }
958
+ }
959
+ }
960
+
961
+ /**
962
+ * Attempt to download a remote file attachment
963
+ *
964
+ * @param string $url URL of item to fetch
965
+ * @param array $post Attachment details
966
+ * @return array|WP_Error Local file location details on success, WP_Error otherwise
967
+ */
968
+ function fetchRemoteFile($url, $post)
969
+ {
970
+ $file_name = basename($url);
971
+
972
+ $upload = wp_upload_bits($file_name, 0, '', $post['upload_date']);
973
+
974
+ if ($upload['error']) {
975
+ return new WP_Error('upload_dir_error', $upload['error']);
976
+ }
977
+
978
+ $response = wp_remote_get($url, array(
979
+ 'stream' => true,
980
+ 'filename' => $upload['file'],
981
+ ));
982
+
983
+ if (is_wp_error($response)) {
984
+ unlink($upload['file']);
985
+ return $response;
986
+ }
987
+
988
+ $code = (int)wp_remote_retrieve_response_code($response);
989
+
990
+ if (200 !== $code) {
991
+ unlink($upload['file']);
992
+ return new WP_Error('import_file_error', sprintf(esc_html__('Remote server returned %1$d %2$s for %3$s', '@@textdomain'), $code, get_status_header_desc($code), $url));
993
+ }
994
+
995
+ $filesize = filesize($upload['file']);
996
+ $headers = wp_remote_retrieve_headers($response);
997
+
998
+ if (isset($headers['content-length']) && $filesize !== (int) $headers['content-length']) {
999
+ unlink($upload['file']);
1000
+ return new WP_Error('import_file_error', esc_html__('Remote file is incorrect size', '@@textdomain'));
1001
+ }
1002
+
1003
+ if (0 === $filesize) {
1004
+ unlink($upload['file']);
1005
+ return new WP_Error('import_file_error', esc_html__('Zero size file downloaded.', '@@textdomain'));
1006
+ }
1007
+
1008
+ $max_size = apply_filters('_wxr_import_attachment_size_limit', 8*MB_IN_BYTES);
1009
+
1010
+ if (!empty($max_size) && $filesize > $max_size) {
1011
+ unlink($upload['file']);
1012
+ $message = sprintf(esc_html__('Remote file is too large, limit is %s.', '@@textdomain'), size_format($max_size));
1013
+ return new WP_Error('import_file_error', $message);
1014
+ }
1015
+
1016
+ return $upload;
1017
+ }
1018
+
1019
+ /**
1020
+ * Re-map imported posts
1021
+ */
1022
+ function remapImportedPosts($mapping_posts)
1023
+ {
1024
+ $imported_users = get_transient('_wxr_imported_users') ? : array();
1025
+ $imported_terms = get_transient('_wxr_imported_terms') ? : array();
1026
+ $imported_posts = get_transient('_wxr_imported_posts') ? : array();
1027
+
1028
+ foreach ($mapping_posts as $post_id => $mapped) {
1029
+ $data = array();
1030
+ $_post = WP_Post::get_instance($post_id);
1031
+ $parent_id = get_post_meta($post_id, '_wxr_import_parent', true);
1032
+ if (! empty($parent_id)) {
1033
+ if (isset($imported_posts[$parent_id])) {
1034
+ $data['post_parent'] = $imported_posts[$parent_id];
1035
+ }
1036
+ }
1037
+ $author_slug = get_post_meta($post_id, '_wxr_import_user_slug', true);
1038
+ if (!empty($author_slug)) {
1039
+ if (isset($imported_users[$author_slug])) {
1040
+ $data['post_author'] = $imported_users[$author_slug];
1041
+ }
1042
+ }
1043
+ if ('nav_menu_item' === $_post->post_type) {
1044
+ $this->remapImportedMenuItem($post_id, $imported_terms, $imported_posts);
1045
+ }
1046
+ if (empty($data)) {
1047
+ continue;
1048
+ }
1049
+ $data['ID'] = $post_id;
1050
+ $result = wp_update_post($data, true);
1051
+ if (is_wp_error($result)) {
1052
+ continue;
1053
+ }
1054
+ delete_post_meta($post_id, '_wxr_import_parent');
1055
+ delete_post_meta($post_id, '_wxr_import_user_slug');
1056
+ }
1057
+ }
1058
+
1059
+ function remapImportedMenuItem($post_id, array $imported_terms, array $imported_posts)
1060
+ {
1061
+ $menu_object_id = get_post_meta($post_id, '_wxr_import_menu_item', true);
1062
+
1063
+ if (empty($menu_object_id)) {
1064
+ return;
1065
+ }
1066
+
1067
+ $menu_item_type = get_post_meta($post_id, '_menu_item_type', true);
1068
+
1069
+ switch ($menu_item_type) {
1070
+ case 'taxonomy':
1071
+ if (isset($imported_terms[$menu_object_id])) {
1072
+ $menu_object = $imported_terms[$menu_object_id];
1073
+ }
1074
+ break;
1075
+ case 'post_type':
1076
+ if (isset($imported_posts[$menu_object_id])) {
1077
+ $menu_object = $imported_posts[$menu_object_id];
1078
+ }
1079
+ break;
1080
+ default:
1081
+ return;
1082
+ }
1083
+
1084
+ if (!empty($menu_object)) {
1085
+ update_post_meta($post_id, '_menu_item_object_id', $menu_object);
1086
+ }
1087
+
1088
+ delete_post_meta($post_id, '_wxr_import_menu_item');
1089
+ }
1090
+
1091
+ /**
1092
+ * Re-map comments
1093
+ */
1094
+ function remapImportedComments(array $comments_to_update)
1095
+ {
1096
+ $imported_users = get_transient('_wxr_imported_users') ? : array();
1097
+ $imported_comments = get_transient('_wxr_imported_comments') ? : array();
1098
+
1099
+ foreach ($comments_to_update as $comment_id => $update) {
1100
+ $data = array();
1101
+ $parent_id = get_comment_meta($comment_id, '_wxr_import_parent', true);
1102
+ if (! empty($parent_id)) {
1103
+ if (isset($imported_comments[$parent_id])) {
1104
+ $data['comment_parent'] = $imported_comments[$parent_id];
1105
+ }
1106
+ }
1107
+ $author_id = get_comment_meta($comment_id, '_wxr_import_user', true);
1108
+ if (! empty($author_id)) {
1109
+ if (isset($imported_users[$author_id])) {
1110
+ $data['user_id'] = $imported_users[$author_id];
1111
+ }
1112
+ }
1113
+ if (empty($data)) {
1114
+ continue;
1115
+ }
1116
+ $data['comment_ID'] = $comment_ID;
1117
+ $result = wp_update_comment($data);
1118
+ if (empty($result)) {
1119
+ continue;
1120
+ }
1121
+
1122
+ delete_comment_meta($comment_id, '_wxr_import_parent');
1123
+ delete_comment_meta($comment_id, '_wxr_import_user');
1124
+ }
1125
+ }
1126
+
1127
+ /**
1128
+ * There is no explicit 'top' or 'root' for a hierarchy of WordPress terms
1129
+ * Terms without a parent, or parent=0 are either unconnected (orphans)
1130
+ * or top-level siblings without an explicit root parent
1131
+ * An unconnected term (orphan) should have a null parent_slug
1132
+ * Top-level siblings without an explicit root parent, shall be identified
1133
+ * with the parent_slug: top
1134
+ * [we'll map parent_slug: top into parent 0]
1135
+ */
1136
+ function remapImportedTerms($terms_to_be_remapped)
1137
+ {
1138
+ $imported_terms = get_transient('_wxr_imported_terms') ? : array();
1139
+
1140
+ foreach ($terms_to_be_remapped as $termid => $term_taxonomy) {
1141
+ $imported_terms['top'] = 0;
1142
+ if(empty($termid) || !is_numeric($termid)) {
1143
+ continue;
1144
+ }
1145
+ $term_id = (int)$termid;
1146
+ if(empty($term_taxonomy)){
1147
+ continue;
1148
+ }
1149
+ $data = array();
1150
+ $parent_slug = get_term_meta($term_id, '_wxr_import_parent', true);
1151
+ if (empty($parent_slug)) {
1152
+ continue;
1153
+ }
1154
+ if (!isset($imported_terms[$parent_slug]) || !is_numeric($imported_terms[$parent_slug])) {
1155
+ continue;
1156
+ }
1157
+ $mapped_parent = $imported_terms[$parent_slug];
1158
+ $termattributes = get_term_by('id', $term_id, $term_taxonomy, ARRAY_A);
1159
+ if (empty($termattributes)) {
1160
+ continue;
1161
+ }
1162
+ if (isset($termattributes['parent']) && $termattributes['parent'] == $mapped_parent) {
1163
+ delete_term_meta($term_id, '_wxr_import_parent');
1164
+ continue;
1165
+ }
1166
+ $termattributes['parent'] = $mapped_parent;
1167
+ $result = wp_update_term($term_id, $termattributes['taxonomy'], $termattributes);
1168
+ if (is_wp_error($result)) {
1169
+ continue;
1170
+ }
1171
+ delete_term_meta($term_id, '_wxr_import_parent');
1172
+ }
1173
+ }
1174
+
1175
+ /**
1176
+ * Does a post already exist?
1177
+ */
1178
+ function postExists(array $post)
1179
+ {
1180
+ static $exists = null;
1181
+
1182
+ if (null === $exists) {
1183
+ global $wpdb;
1184
+ $exists = array();
1185
+ $db_posts = $wpdb->get_results("SELECT ID, guid FROM {$wpdb->posts}");
1186
+ foreach ($db_posts as &$db_post) {
1187
+ $exists[$db_post->guid] = $db_post->ID;
1188
+ }
1189
+ }
1190
+
1191
+ return isset($exists[$post['guid']]) ? $exists[$post['guid']] : false;
1192
+ }
1193
+
1194
+ /**
1195
+ * Does a comment already exist?
1196
+ */
1197
+ function commentExists($comment)
1198
+ {
1199
+ static $exists = null;
1200
+
1201
+ if (null === $exists) {
1202
+ global $wpdb;
1203
+ $exists = array();
1204
+ $db_comments = $wpdb->get_results("SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}");
1205
+ foreach ($db_comments as &$db_comment) {
1206
+ $db_hash = crc32($db_comment->comment_author . ':' . $db_comment->comment_date);
1207
+ $exists[$db_hash] = $db_comment->comment_ID;
1208
+ }
1209
+ }
1210
+
1211
+ $hash = crc32($comment['comment_author'] . ':' . $comment['comment_date']);
1212
+
1213
+ return isset($exists[$hash]) ? $exists[$hash] : false;
1214
+ }
1215
+
1216
+ /**
1217
+ * Does a term already exist?
1218
+ */
1219
+ function termExists(array $term)
1220
+ {
1221
+ static $exists = null;
1222
+
1223
+ if (null === $exists) {
1224
+ global $wpdb;
1225
+ $exists = array();
1226
+ $db_terms = $wpdb->get_results("SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id");
1227
+ foreach ($db_terms as &$db_term) {
1228
+ $db_hash = crc32($db_term->taxonomy . ':' . $db_term->slug);
1229
+ $exists[$db_hash] = $db_term->term_id;
1230
+ }
1231
+ }
1232
+
1233
+ $hash = crc32($term['taxonomy'] . ':' . $term['slug']);
1234
+
1235
+ return isset($exists[$hash]) ? $exists[$hash] : false;
1236
+ }
1237
+
1238
+ /**
1239
+ * Sideload customizer image
1240
+ *
1241
+ * @author ProteusThemes
1242
+ */
1243
+ function fetchCustomizeImage($file)
1244
+ {
1245
+ $data = new \stdClass();
1246
+
1247
+ if ( !function_exists('media_handle_sideload') ) {
1248
+ require ABSPATH . 'wp-admin/includes/media.php';
1249
+ require ABSPATH . 'wp-admin/includes/file.php';
1250
+ require ABSPATH . 'wp-admin/includes/image.php';
1251
+ }
1252
+
1253
+ if ( !empty($file) ) {
1254
+ preg_match('/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches);
1255
+ $file_array = array();
1256
+ $file_array['name'] = basename($matches[0]);
1257
+ $file_array['tmp_name'] = download_url($file);
1258
+ if ( is_wp_error($file_array['tmp_name']) ) {
1259
+ return $file_array['tmp_name'];
1260
+ }
1261
+ $id = media_handle_sideload($file_array, 0);
1262
+ if ( is_wp_error($id) ) {
1263
+ unlink($file_array['tmp_name']);
1264
+ return $id;
1265
+ }
1266
+ $meta = wp_get_attachment_metadata($id);
1267
+ $data->attachment_id = $id;
1268
+ $data->url = wp_get_attachment_url($id);
1269
+ $data->thumbnail_url = wp_get_attachment_thumb_url($id);
1270
+ $data->height = $meta['height'];
1271
+ $data->width = $meta['width'];
1272
+ }
1273
+
1274
+ return $data;
1275
+ }
1276
+
1277
+ /**
1278
+ * register_custom_taxonomy
1279
+ *
1280
+ * To bypass theme check
1281
+ *
1282
+ * @see https://developer.wordpress.org/reference/functions/register_taxonomy/
1283
+ */
1284
+ function register_custom_taxonomy($taxonomy, $object_type, $args = array())
1285
+ {
1286
+ global $wp_taxonomies;
1287
+
1288
+ $args = wp_parse_args($args);
1289
+
1290
+ if ( !is_array($wp_taxonomies) ) {
1291
+ $wp_taxonomies = array();
1292
+ }
1293
+
1294
+ if ( empty($taxonomy) || strlen( $taxonomy ) > 32 ) {
1295
+ _doing_it_wrong( __FUNCTION__, esc_html__( 'Taxonomy names must be between 1 and 32 characters in length.', '@@textdomain' ), '4.2.0' );
1296
+ return new WP_Error( 'taxonomy_length_invalid', esc_html__( 'Taxonomy names must be between 1 and 32 characters in length.', '@@textdomain' ) );
1297
+ }
1298
+
1299
+ $taxonomy_object = new WP_Taxonomy( $taxonomy, $object_type, $args );
1300
+ $taxonomy_object->add_rewrite_rules();
1301
+ $wp_taxonomies[$taxonomy] = $taxonomy_object;
1302
+ $taxonomy_object->add_hooks();
1303
+
1304
+ do_action( 'registered_taxonomy', $taxonomy, $object_type, (array)$taxonomy_object );
1305
+ }
1306
+
1307
+ /**
1308
+ * Added to http_request_timeout filter to force timeout at 60 seconds during import
1309
+ *
1310
+ * @access protected
1311
+ * @return int 60
1312
+ */
1313
+ function _bumpHttpRequestTimeout($val)
1314
+ {
1315
+ return 120;
1316
+ }
1317
+
1318
+ /**
1319
+ * Callback for `usort` to sort comments by ID
1320
+ *
1321
+ * @param array $a Comment data for the first comment
1322
+ * @param array $b Comment data for the second comment
1323
+ * @return int
1324
+ */
1325
+ function _sortCommentsById($a, $b)
1326
+ {
1327
+ if (empty($a['comment_id'])) {
1328
+ return 1;
1329
+ }
1330
+
1331
+ if (empty($b['comment_id'])) {
1332
+ return -1;
1333
+ }
1334
+
1335
+ return $a['comment_id'] - $b['comment_id'];
1336
+ }
1337
+ }
inc/merlin-wp/includes/class-merlin-xml-parser.php ADDED
@@ -0,0 +1,638 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Merlin WP
4
+ * Better WordPress Theme Onboarding
5
+ *
6
+ * @package Merlin WP
7
+ * @version @@pkg.version
8
+ * @link https://merlinwp.com/
9
+ * @author Richard Tabor, from ThemeBeans.com
10
+ * @copyright Copyright (c) 2017, Merlin WP of Inventionn LLC
11
+ * @license Licensed GPLv3 for open source use
12
+ */
13
+
14
+ /**
15
+ * XML Parser
16
+ */
17
+ class Merlin_WXR_Parser {
18
+
19
+ /**
20
+ * Parsing file
21
+ *
22
+ * @var string $file The XML file.
23
+ */
24
+ protected $file;
25
+
26
+ /**
27
+ * Constructor
28
+ *
29
+ * @var string $file Full path to XML file.
30
+ */
31
+ function __construct() { }
32
+
33
+ /**
34
+ * Parse
35
+ *
36
+ * @param string $file Maybe parse a custom file instead of the constructed file.
37
+ */
38
+ function parse( $file ) {
39
+ $file = wp_normalize_path( $file );
40
+
41
+ if ( empty( $file ) ) {
42
+ return new WP_Error( 'WXR_parse_error', esc_html__( 'Invalid WXR file.', '@@textdomain' ) );
43
+ }
44
+
45
+ if ( extension_loaded( 'simplexml' ) ) {
46
+ return $this->simplexml_parse( $file );
47
+ } elseif ( extension_loaded( 'xmlreader' ) ) {
48
+ return $this->xml_reader_parse( $file );
49
+ } else {
50
+ return new WP_Error( 'WXR_parse_error', esc_html__( 'No XML parser found.', '@@textdomain' ) );
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Parse XML file
56
+ *
57
+ * @param string $file Maybe parse a custom file instead of the constructed file.
58
+ */
59
+ protected function simplexml_parse( $file ) {
60
+ $authors = $posts = $categories = $tags = $terms = array();
61
+
62
+ libxml_use_internal_errors( true );
63
+
64
+ $xml = simplexml_load_file( $file );
65
+
66
+ if ( ! $xml ) {
67
+ return new WP_Error( 'WXR_parse_error', esc_html__( 'Invalid WXR file.', '@@textdomain' ), libxml_get_errors() );
68
+ }
69
+
70
+ $wxr_version = $xml->xpath( '/rss/channel/wp:wxr_version' );
71
+ $wxr_version = (string) trim( $wxr_version[0] );
72
+ $wxr_compare = version_compare( $wxr_version, '1.2' );
73
+
74
+ if ( ! $wxr_version || 1 === $wxr_compare ) {
75
+ return new WP_Error( 'WXR_parse_error', esc_html__( 'Invalid WXR file.', '@@textdomain' ) );
76
+ }
77
+
78
+ if ( ! preg_match( '/^\d+\.\d+$/', $wxr_version ) ) {
79
+ return new WP_Error( 'WXR_parse_error', esc_html__( 'Invalid WXR file.', '@@textdomain' ) );
80
+ }
81
+
82
+ $base_url = $xml->xpath( '/rss/channel/wp:base_site_url' );
83
+ $base_url = (string) trim( $base_url[0] );
84
+ $namespaces = $xml->getDocNamespaces();
85
+
86
+ if ( ! isset( $namespaces['wp'] ) ) {
87
+ $namespaces['wp'] = 'http://wordpress.org/export/' . $wxr_version . '/';
88
+ }
89
+
90
+ if ( ! isset( $namespaces['excerpt'] ) ) {
91
+ $namespaces['excerpt'] = 'http://wordpress.org/export/' . $wxr_version . '/excerpt/';
92
+ }
93
+
94
+ $wp_authors = $xml->xpath( '/rss/channel/wp:author' );
95
+
96
+ foreach ( $wp_authors as $author ) {
97
+ $author = $author->children( $namespaces['wp'] );
98
+ $authors[] = array(
99
+ 'id' => (int) $author->author_id,
100
+ 'login' => (string) $author->author_login,
101
+ 'email' => (string) $author->author_email,
102
+ 'first_name' => (string) $author->author_first_name,
103
+ 'last_name' => (string) $author->author_last_name,
104
+ 'display_name' => (string) $author->author_display_name,
105
+ );
106
+ }
107
+
108
+ $wp_cats = $xml->xpath( '/rss/channel/wp:category' );
109
+
110
+ foreach ( $wp_cats as $cat ) {
111
+ $cat1 = $cat->children( $namespaces['wp'] );
112
+ $cat2 = array(
113
+ 'id' => (int) $cat1->term_id,
114
+ 'name' => (string) $cat1->cat_name,
115
+ 'slug' => (string) $cat1->category_nicename,
116
+ 'parent' => (string) $cat1->category_parent,
117
+ 'taxonomy' => 'category',
118
+ 'description' => (string) $cat1->category_description,
119
+ );
120
+ foreach ( $cat1->termmeta as $meta ) {
121
+ $cat2['meta'][] = array(
122
+ 'key' => (string) $meta->meta_key,
123
+ 'value' => (string) $meta->meta_value,
124
+ );
125
+ }
126
+ $categories[] = $cat2;
127
+ }
128
+
129
+ $wp_tags = $xml->xpath( '/rss/channel/wp:tag' );
130
+
131
+ foreach ( $wp_tags as $tag ) {
132
+ $tag1 = $tag->children( $namespaces['wp'] );
133
+ $tag2 = array(
134
+ 'id' => (int) $tag1->term_id,
135
+ 'slug' => (string) $tag1->tag_slug,
136
+ 'name' => (string) $tag1->tag_name,
137
+ 'taxonomy' => 'post_tag',
138
+ 'description' => (string) $tag1->tag_description,
139
+ );
140
+ foreach ( $tag1->termmeta as $meta ) {
141
+ $tag2['meta'][] = array(
142
+ 'key' => (string) $meta->meta_key,
143
+ 'value' => (string) $meta->meta_value,
144
+ );
145
+ }
146
+ $tags[] = $tag2;
147
+ }
148
+
149
+ $wp_terms = $xml->xpath( '/rss/channel/wp:term' );
150
+
151
+ foreach ( $wp_terms as $term ) {
152
+ $term1 = $term->children( $namespaces['wp'] );
153
+ $term2 = array(
154
+ 'id' => (int) $term1->term_id,
155
+ 'slug' => (string) $term1->term_slug,
156
+ 'parent' => (string) $term1->term_parent,
157
+ 'name' => (string) $term1->term_name,
158
+ 'taxonomy' => (string) $term1->term_taxonomy,
159
+ 'description' => (string) $term1->term_description,
160
+ );
161
+ foreach ( $term1->termmeta as $meta ) {
162
+ $term2['meta'][] = array(
163
+ 'key' => (string) $meta->meta_key,
164
+ 'value' => (string) $meta->meta_value,
165
+ );
166
+ }
167
+ $terms[] = $term2;
168
+ }
169
+
170
+ foreach ( $xml->channel->item as $item ) {
171
+ $dc = $item->children( 'http://purl.org/dc/elements/1.1/' );
172
+ $wp = $item->children( $namespaces['wp'] );
173
+ $content = $item->children( 'http://purl.org/rss/1.0/modules/content/' );
174
+ $excerpt = $item->children( $namespaces['excerpt'] );
175
+ $post = array(
176
+ 'post_title' => (string) $item->title,
177
+ 'guid' => (string) $item->guid,
178
+ 'post_author' => (string) $dc->creator,
179
+ 'post_content' => (string) $content->encoded,
180
+ 'post_excerpt' => (string) $excerpt->encoded,
181
+ 'post_id' => (int) $wp->post_id,
182
+ 'post_date' => (string) $wp->post_date,
183
+ 'post_date_gmt' => (string) $wp->post_date_gmt,
184
+ 'comment_status' => (string) $wp->comment_status,
185
+ 'ping_status' => (string) $wp->ping_status,
186
+ 'post_name' => (string) $wp->post_name,
187
+ 'post_status' => (string) $wp->status,
188
+ 'post_parent' => (int) $wp->post_parent,
189
+ 'menu_order' => (int) $wp->menu_order,
190
+ 'post_type' => (string) $wp->post_type,
191
+ 'post_password' => (string) $wp->post_password,
192
+ 'is_sticky' => (int) $wp->is_sticky,
193
+ );
194
+
195
+ if ( isset( $wp->attachment_url ) ) {
196
+ $post['attachment_url'] = (string) $wp->attachment_url;
197
+ }
198
+ foreach ( $item->category as $c ) {
199
+ $att = $c->attributes();
200
+ if ( isset( $att['nicename'] ) ) {
201
+ $post['terms'][] = array(
202
+ 'name' => (string) $c,
203
+ 'slug' => (string) $att['nicename'],
204
+ 'taxonomy' => (string) $att['domain'],
205
+ );
206
+ }
207
+ }
208
+ foreach ( $wp->postmeta as $meta ) {
209
+ $post['meta'][] = array(
210
+ 'key' => (string) $meta->meta_key,
211
+ 'value' => (string) $meta->meta_value,
212
+ );
213
+ }
214
+ foreach ( $wp->comment as $comment ) {
215
+ $meta = array();
216
+ if ( isset( $comment->commentmeta ) ) {
217
+ foreach ( $comment->commentmeta as $m ) {
218
+ $meta[] = array(
219
+ 'key' => (string) $m->meta_key,
220
+ 'value' => (string) $m->meta_value,
221
+ );
222
+ }
223
+ }
224
+ $post['comments'][] = array(
225
+ 'comment_id' => (int) $comment->comment_id,
226
+ 'comment_author' => (string) $comment->comment_author,
227
+ 'comment_author_email' => (string) $comment->comment_author_email,
228
+ 'comment_author_IP' => (string) $comment->comment_author_IP,
229
+ 'comment_author_url' => (string) $comment->comment_author_url,
230
+ 'comment_date' => (string) $comment->comment_date,
231
+ 'comment_date_gmt' => (string) $comment->comment_date_gmt,
232
+ 'comment_content' => (string) $comment->comment_content,
233
+ 'comment_approved' => (string) $comment->comment_approved,
234
+ 'comment_type' => (string) $comment->comment_type,
235
+ 'comment_parent' => (int) $comment->comment_parent,
236
+ 'comment_user_id' => (int) $comment->comment_user_id,
237
+ 'commentmeta' => $meta,
238
+ );
239
+ }
240
+ $posts[] = $post;
241
+ }
242
+
243
+ return array(
244
+ 'users' => $authors,
245
+ 'categories' => $categories,
246
+ 'tags' => $tags,
247
+ 'terms' => $terms,
248
+ 'posts' => $posts,
249
+ 'baseurl' => $base_url,
250
+ 'version' => $wxr_version,
251
+ );
252
+ }
253
+
254
+ /**
255
+ * Parse XML with XMLReader
256
+ *
257
+ * @param string $file Maybe parse a custom file instead of the constructed file.
258
+ */
259
+ protected function xml_reader_parse( $file ) {
260
+ $data = array();
261
+ $reader = new XMLReader();
262
+ $status = $reader->open( $file );
263
+
264
+ if ( ! $status ) {
265
+ return new WP_Error( 'WXR_parse_error', esc_html__( 'Could not open the file for parsing', '@@textdomain' ) );
266
+ }
267
+
268
+ while ( $reader->read() ) {
269
+ if ( XMLReader::ELEMENT !== $reader->nodeType ) {
270
+ continue;
271
+ }
272
+ switch ( $reader->name ) {
273
+ case 'wp:wxr_version':
274
+ $version = $reader->readString();
275
+
276
+ if ( version_compare( $version, '1.2', '>' ) ) {
277
+ return new WP_Error( 'WXR_parse_error', esc_html__( 'Invalid WXR file.', '@@textdomain' ) );
278
+ }
279
+
280
+ $data['version'] = $version;
281
+ $reader->next();
282
+ break;
283
+ case 'wp:base_site_url':
284
+ $base_url = $reader->readString();
285
+ $data['baseurl'] = $base_url;
286
+ $reader->next();
287
+ break;
288
+ case 'wp:author':
289
+ $node = $reader->expand();
290
+ $data['users'][] = $this->parseUser( $node );
291
+ $reader->next();
292
+ break;
293
+ case 'wp:category':
294
+ $node = $reader->expand();
295
+ $data['categories'][] = $this->parseTerm( $node, 'category' );
296
+ $reader->next();
297
+ break;
298
+ case 'wp:tag':
299
+ $node = $reader->expand();
300
+ $data['tags'][] = $this->parseTerm( $node, 'tag' );
301
+ $reader->next();
302
+ break;
303
+ case 'wp:term':
304
+ $node = $reader->expand();
305
+ $data['terms'][] = $this->parseTerm( $node );
306
+ $reader->next();
307
+ break;
308
+ case 'item':
309
+ $node = $reader->expand();
310
+ $data['posts'][] = $this->parsePost( $node );
311
+ $reader->next();
312
+ break;
313
+ default:
314
+ break;
315
+ }
316
+ }
317
+
318
+ return $data;
319
+ }
320
+
321
+ /**
322
+ * Parse author node
323
+ */
324
+ protected function parseUser( $node ) {
325
+
326
+ $data = $meta = array();
327
+
328
+ foreach ( $node->childNodes as $child ) {
329
+ if ( XML_ELEMENT_NODE !== $child->nodeType ) {
330
+ continue;
331
+ }
332
+ switch ( $child->tagName) {
333
+ case 'wp:author_id':
334
+ $data['id'] = (int)$child->textContent;
335
+ break;
336
+ case 'wp:author_login':
337
+ $data['login'] = $child->textContent;
338
+ break;
339
+ case 'wp:author_email':
340
+ $data['email'] = $child->textContent;
341
+ break;
342
+ case 'wp:author_display_name':
343
+ $data['display_name'] = $child->textContent;
344
+ break;
345
+ case 'wp:author_first_name':
346
+ $data['first_name'] = $child->textContent;
347
+ break;
348
+ case 'wp:author_last_name':
349
+ $data['last_name'] = $child->textContent;
350
+ break;
351
+ default:
352
+ break;
353
+ }
354
+ }
355
+
356
+ $data['meta'] = $meta;
357
+
358
+ return $data;
359
+ }
360
+
361
+ /**
362
+ * Parse term node
363
+ */
364
+ protected function parseTerm( $node, $type = 'term' ) {
365
+
366
+ $data = $meta = array();
367
+
368
+ $tag_name = array(
369
+ 'id' => 'wp:term_id',
370
+ 'taxonomy' => 'wp:term_taxonomy',
371
+ 'slug' => 'wp:term_slug',
372
+ 'parent' => 'wp:term_parent',
373
+ 'name' => 'wp:term_name',
374
+ 'description' => 'wp:term_description',
375
+ );
376
+
377
+ switch ( $type ) {
378
+ case 'category':
379
+ $tag_name['slug'] = 'wp:category_nicename';
380
+ $tag_name['parent'] = 'wp:category_parent';
381
+ $tag_name['name'] = 'wp:cat_name';
382
+ $tag_name['description'] = 'wp:category_description';
383
+ $tag_name['taxonomy'] = null;
384
+ $data['taxonomy'] = 'category';
385
+ break;
386
+ case 'tag':
387
+ $tag_name['slug'] = 'wp:tag_slug';
388
+ $tag_name['parent'] = null;
389
+ $tag_name['name'] = 'wp:tag_name';
390
+ $tag_name['description'] = 'wp:tag_description';
391
+ $tag_name['taxonomy'] = null;
392
+ $data['taxonomy'] = 'post_tag';
393
+ break;
394
+ default:
395
+ break;
396
+ }
397
+
398
+ foreach ( $node->childNodes as $child ) {
399
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
400
+ continue;
401
+ }
402
+ $key = array_search( $child->tagName, $tag_name );
403
+ if ( $key ) {
404
+ $data[$key] = $child->textContent;
405
+ } elseif ( $child->tagName === 'wp:termmeta' ) {
406
+ $meta_item = $this->parseMeta( $child );
407
+ if ( !empty( $meta_item ) ) {
408
+ $meta[] = $meta_item;
409
+ }
410
+ }
411
+ }
412
+
413
+ if ( empty( $data['taxonomy'] ) ) {
414
+ return null;
415
+ }
416
+
417
+ $data['meta'] = $meta;
418
+
419
+ return $data;
420
+ }
421
+
422
+ /**
423
+ * Parse post node
424
+ */
425
+ protected function parsePost( $node ) {
426
+ $data = $meta = $comments = $terms = array();
427
+
428
+ foreach ($node->childNodes as $child) {
429
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
430
+ continue;
431
+ }
432
+ switch ($child->tagName) {
433
+ case 'wp:post_type':
434
+ $data['post_type'] = $child->textContent;
435
+ break;
436
+ case 'title':
437
+ $data['post_title'] = $child->textContent;
438
+ break;
439
+ case 'guid':
440
+ $data['guid'] = $child->textContent;
441
+ break;
442
+ case 'dc:creator':
443
+ $data['post_author'] = $child->textContent;
444
+ break;
445
+ case 'content:encoded':
446
+ $data['post_content'] = $child->textContent;
447
+ break;
448
+ case 'excerpt:encoded':
449
+ $data['post_excerpt'] = $child->textContent;
450
+ break;
451
+ case 'wp:post_id':
452
+ $data['post_id'] = (int)$child->textContent;
453
+ break;
454
+ case 'wp:post_date':
455
+ $data['post_date'] = $child->textContent;
456
+ break;
457
+ case 'wp:post_date_gmt':
458
+ $data['post_date_gmt'] = $child->textContent;
459
+ break;
460
+ case 'wp:comment_status':
461
+ $data['comment_status'] = $child->textContent;
462
+ break;
463
+ case 'wp:ping_status':
464
+ $data['ping_status'] = $child->textContent;
465
+ break;
466
+ case 'wp:post_name':
467
+ $data['post_name'] = $child->textContent;
468
+ break;
469
+ case 'wp:status':
470
+ $data['post_status'] = $child->textContent;
471
+ break;
472
+ case 'wp:post_parent':
473
+ $data['post_parent'] = (int)$child->textContent;
474
+ break;
475
+ case 'wp:menu_order':
476
+ $data['menu_order'] = $child->textContent;
477
+ break;
478
+ case 'wp:post_password':
479
+ $data['post_password'] = $child->textContent;
480
+ break;
481
+ case 'wp:is_sticky':
482
+ $data['is_sticky'] = $child->textContent;
483
+ break;
484
+ case 'wp:attachment_url':
485
+ $data['attachment_url'] = $child->textContent;
486
+ break;
487
+ case 'wp:postmeta':
488
+ $meta_item = $this->parseMeta( $child );
489
+ if ( !empty($meta_item) ) {
490
+ $meta[] = $meta_item;
491
+ }
492
+ break;
493
+ case 'wp:comment':
494
+ $comment_item = $this->parseComment( $child );
495
+ if ( ! empty( $comment_item ) ) {
496
+ $comments[] = $comment_item;
497
+ }
498
+ break;
499
+ case 'category':
500
+ $term_item = $this->parsePostTerm( $child );
501
+ if ( ! empty( $term_item ) ) {
502
+ $terms[] = $term_item;
503
+ }
504
+ break;
505
+ default:
506
+ break;
507
+ }
508
+ }
509
+
510
+ $data['terms'] = $terms;
511
+ $data['comments'] = $comments;
512
+ $data['meta'] = $meta;
513
+
514
+ return $data;
515
+ }
516
+
517
+ /**
518
+ * Parse meta node
519
+ */
520
+ protected function parseMeta( $node ) {
521
+
522
+ foreach ( $node->childNodes as $child ) {
523
+
524
+ if ( XML_ELEMENT_NODE !== $child->nodeType ) {
525
+ continue;
526
+ }
527
+ switch ( $child->tagName ) {
528
+ case 'wp:meta_key':
529
+ $key = $child->textContent;
530
+ break;
531
+ case 'wp:meta_value':
532
+ $value = $child->textContent;
533
+ break;
534
+ default:
535
+ break;
536
+ }
537
+ }
538
+
539
+ if ( empty( $key ) || empty( $value ) ) {
540
+ return null;
541
+ }
542
+
543
+ return array(
544
+ 'key' => $key,
545
+ 'value' => $value,
546
+ );
547
+ }
548
+
549
+ /**
550
+ * Parse comment node
551
+ */
552
+ protected function parseComment( $node ) {
553
+ $data = array(
554
+ 'commentmeta' => array(),
555
+ );
556
+ foreach ( $node->childNodes as $child ) {
557
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
558
+ continue;
559
+ }
560
+ switch ( $child->tagName ) {
561
+ case 'wp:comment_id':
562
+ $data['comment_id'] = (int)$child->textContent;
563
+ break;
564
+ case 'wp:comment_author':
565
+ $data['comment_author'] = $child->textContent;
566
+ break;
567
+ case 'wp:comment_author_email':
568
+ $data['comment_author_email'] = $child->textContent;
569
+ break;
570
+ case 'wp:comment_author_IP':
571
+ $data['comment_author_IP'] = $child->textContent;
572
+ break;
573
+ case 'wp:comment_author_url':
574
+ $data['comment_author_url'] = $child->textContent;
575
+ break;
576
+ case 'wp:comment_user_id':
577
+ $data['comment_user_id'] = (int)$child->textContent;
578
+ break;
579
+ case 'wp:comment_date':
580
+ $data['comment_date'] = $child->textContent;
581
+ break;
582
+ case 'wp:comment_date_gmt':
583
+ $data['comment_date_gmt'] = $child->textContent;
584
+ break;
585
+ case 'wp:comment_content':
586
+ $data['comment_content'] = $child->textContent;
587
+ break;
588
+ case 'wp:comment_approved':
589
+ $data['comment_approved'] = $child->textContent;
590
+ break;
591
+ case 'wp:comment_type':
592
+ $data['comment_type'] = $child->textContent;
593
+ break;
594
+ case 'wp:comment_parent':
595
+ $data['comment_parent'] = (int)$child->textContent;
596
+ break;
597
+ case 'wp:commentmeta':
598
+ $meta_item = $this->parseMeta( $child );
599
+ if ( ! empty( $meta_item ) ) {
600
+ $data['commentmeta'][] = $meta_item;
601
+ }
602
+ break;
603
+ default:
604
+ break;
605
+ }
606
+ }
607
+
608
+ return $data;
609
+ }
610
+
611
+ /**
612
+ * Parse post term
613
+ */
614
+ protected function parsePostTerm( $node ) {
615
+ $data = array(
616
+ 'taxonomy' => 'category',
617
+ );
618
+
619
+ if ( $node->hasAttribute( 'domain' ) ) {
620
+ $data['taxonomy'] = $node->getAttribute( 'domain' );
621
+ }
622
+ if ( $node->hasAttribute( 'nicename' ) ) {
623
+ $data['slug'] = $node->getAttribute( 'nicename' );
624
+ }
625
+
626
+ $data['name'] = $node->textContent;
627
+
628
+ if ( empty( $data['slug'] ) ) {
629
+ return null;
630
+ }
631
+
632
+ if ( 'tag' === $data['taxonomy'] ) {
633
+ $data['taxonomy'] = 'post_tag';
634
+ }
635
+
636
+ return $data;
637
+ }
638
+ }
inc/parsers.php DELETED
@@ -1,691 +0,0 @@
1
- <?php
2
- /**
3
- * WordPress eXtended RSS file parser implementations
4
- *
5
- * @package WordPress
6
- * @subpackage Importer
7
- */
8
-
9
- /**
10
- * WordPress Importer class for managing parsing of WXR files.
11
- */
12
- class WXR_Parser {
13
- function parse( $file ) {
14
- // Attempt to use proper XML parsers first
15
- if ( extension_loaded( 'simplexml' ) ) {
16
- $parser = new WXR_Parser_SimpleXML;
17
- $result = $parser->parse( $file );
18
-
19
- // If SimpleXML succeeds or this is an invalid WXR file then return the results
20
- if ( ! is_wp_error( $result ) || 'SimpleXML_parse_error' != $result->get_error_code() )
21
- return $result;
22
- } else if ( extension_loaded( 'xml' ) ) {
23
- $parser = new WXR_Parser_XML;
24
- $result = $parser->parse( $file );
25
-
26
- // If XMLParser succeeds or this is an invalid WXR file then return the results
27
- if ( ! is_wp_error( $result ) || 'XML_parse_error' != $result->get_error_code() )
28
- return $result;
29
- }
30
-
31
- // We have a malformed XML file, so display the error and fallthrough to regex
32
- if ( isset($result) && defined('IMPORT_DEBUG') && IMPORT_DEBUG ) {
33
- echo '<pre>';
34
- if ( 'SimpleXML_parse_error' == $result->get_error_code() ) {
35
- foreach ( $result->get_error_data() as $error )
36
- echo $error->line . ':' . $error->column . ' ' . esc_html( $error->message ) . "\n";
37
- } else if ( 'XML_parse_error' == $result->get_error_code() ) {
38
- $error = $result->get_error_data();
39
- echo $error[0] . ':' . $error[1] . ' ' . esc_html( $error[2] );
40
- }
41
- echo '</pre>';
42
- echo '<p><strong>' . __( 'There was an error when reading this WXR file', 'wordpress-importer' ) . '</strong><br />';
43
- echo __( 'Details are shown above. The importer will now try again with a different parser...', 'wordpress-importer' ) . '</p>';
44
- }
45
-
46
- // use regular expressions if nothing else available or this is bad XML
47
- $parser = new WXR_Parser_Regex;
48
- return $parser->parse( $file );
49
- }
50
- }
51
-
52
- /**
53
- * WXR Parser that makes use of the SimpleXML PHP extension.
54
- */
55
- class WXR_Parser_SimpleXML {
56
- function parse( $file ) {
57
- $authors = $posts = $categories = $tags = $terms = array();
58
-
59
- $internal_errors = libxml_use_internal_errors(true);
60
-
61
- $dom = new DOMDocument;
62
- $old_value = null;
63
- if ( function_exists( 'libxml_disable_entity_loader' ) ) {
64
- $old_value = libxml_disable_entity_loader( true );
65
- }
66
- $success = $dom->loadXML( file_get_contents( $file ) );
67
- if ( ! is_null( $old_value ) ) {
68
- libxml_disable_entity_loader( $old_value );
69
- }
70
-
71
- if ( ! $success || isset( $dom->doctype ) ) {
72
- return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wordpress-importer' ), libxml_get_errors() );
73
- }
74
-
75
- $xml = simplexml_import_dom( $dom );
76
- unset( $dom );
77
-
78
- // halt if loading produces an error
79
- if ( ! $xml )
80
- return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wordpress-importer' ), libxml_get_errors() );
81
-
82
- $wxr_version = $xml->xpath('/rss/channel/wp:wxr_version');
83
- if ( ! $wxr_version )
84
- return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
85
-
86
- $wxr_version = (string) trim( $wxr_version[0] );
87
- // confirm that we are dealing with the correct file format
88
- if ( ! preg_match( '/^\d+\.\d+$/', $wxr_version ) )
89
- return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
90
-
91
- $base_url = $xml->xpath('/rss/channel/wp:base_site_url');
92
- $base_url = (string) trim( $base_url[0] );
93
-
94
- $namespaces = $xml->getDocNamespaces();
95
- if ( ! isset( $namespaces['wp'] ) )
96
- $namespaces['wp'] = 'http://wordpress.org/export/1.1/';
97
- if ( ! isset( $namespaces['excerpt'] ) )
98
- $namespaces['excerpt'] = 'http://wordpress.org/export/1.1/excerpt/';
99
-
100
- // grab authors
101
- foreach ( $xml->xpath('/rss/channel/wp:author') as $author_arr ) {
102
- $a = $author_arr->children( $namespaces['wp'] );
103
- $login = (string) $a->author_login;
104
- $authors[$login] = array(
105
- 'author_id' => (int) $a->author_id,
106
- 'author_login' => $login,
107
- 'author_email' => (string) $a->author_email,
108
- 'author_display_name' => (string) $a->author_display_name,
109
- 'author_first_name' => (string) $a->author_first_name,
110
- 'author_last_name' => (string) $a->author_last_name
111
- );
112
- }
113
-
114
- // grab cats, tags and terms
115
- foreach ( $xml->xpath('/rss/channel/wp:category') as $term_arr ) {
116
- $t = $term_arr->children( $namespaces['wp'] );
117
- $category = array(
118
- 'term_id' => (int) $t->term_id,
119
- 'category_nicename' => (string) $t->category_nicename,
120
- 'category_parent' => (string) $t->category_parent,
121
- 'cat_name' => (string) $t->cat_name,
122
- 'category_description' => (string) $t->category_description
123
- );
124
-
125
- foreach ( $t->termmeta as $meta ) {
126
- $category['termmeta'][] = array(
127
- 'key' => (string) $meta->meta_key,
128
- 'value' => (string) $meta->meta_value
129
- );
130
- }
131
-
132
- $categories[] = $category;
133
- }
134
-
135
- foreach ( $xml->xpath('/rss/channel/wp:tag') as $term_arr ) {
136
- $t = $term_arr->children( $namespaces['wp'] );
137
- $tag = array(
138
- 'term_id' => (int) $t->term_id,
139
- 'tag_slug' => (string) $t->tag_slug,
140
- 'tag_name' => (string) $t->tag_name,
141
- 'tag_description' => (string) $t->tag_description
142
- );
143
-
144
- foreach ( $t->termmeta as $meta ) {
145
- $tag['termmeta'][] = array(
146
- 'key' => (string) $meta->meta_key,
147
- 'value' => (string) $meta->meta_value
148
- );
149
- }
150
-
151
- $tags[] = $tag;
152
- }
153
-
154
- foreach ( $xml->xpath('/rss/channel/wp:term') as $term_arr ) {
155
- $t = $term_arr->children( $namespaces['wp'] );
156
- $term = array(
157
- 'term_id' => (int) $t->term_id,
158
- 'term_taxonomy' => (string) $t->term_taxonomy,
159
- 'slug' => (string) $t->term_slug,
160
- 'term_parent' => (string) $t->term_parent,
161
- 'term_name' => (string) $t->term_name,
162
- 'term_description' => (string) $t->term_description
163
- );
164
-
165
- foreach ( $t->termmeta as $meta ) {
166
- $term['termmeta'][] = array(
167
- 'key' => (string) $meta->meta_key,
168
- 'value' => (string) $meta->meta_value
169
- );
170
- }
171
-
172
- $terms[] = $term;
173
- }
174
-
175
- // grab posts
176
- foreach ( $xml->channel->item as $item ) {
177
- $post = array(
178
- 'post_title' => (string) $item->title,
179
- 'guid' => (string) $item->guid,
180
- );
181
-
182
- $dc = $item->children( 'http://purl.org/dc/elements/1.1/' );
183
- $post['post_author'] = (string) $dc->creator;
184
-
185
- $content = $item->children( 'http://purl.org/rss/1.0/modules/content/' );
186
- $excerpt = $item->children( $namespaces['excerpt'] );
187
- $post['post_content'] = (string) $content->encoded;
188
- $post['post_excerpt'] = (string) $excerpt->encoded;
189
-
190
- $wp = $item->children( $namespaces['wp'] );
191
- $post['post_id'] = (int) $wp->post_id;
192
- $post['post_date'] = (string) $wp->post_date;
193
- $post['post_date_gmt'] = (string) $wp->post_date_gmt;
194
- $post['comment_status'] = (string) $wp->comment_status;
195
- $post['ping_status'] = (string) $wp->ping_status;
196
- $post['post_name'] = (string) $wp->post_name;
197
- $post['status'] = (string) $wp->status;
198
- $post['post_parent'] = (int) $wp->post_parent;
199
- $post['menu_order'] = (int) $wp->menu_order;
200
- $post['post_type'] = (string) $wp->post_type;
201
- $post['post_password'] = (string) $wp->post_password;
202
- $post['is_sticky'] = (int) $wp->is_sticky;
203
-
204
- if ( isset($wp->attachment_url) )
205
- $post['attachment_url'] = (string) $wp->attachment_url;
206
-
207
- foreach ( $item->category as $c ) {
208
- $att = $c->attributes();
209
- if ( isset( $att['nicename'] ) )
210
- $post['terms'][] = array(
211
- 'name' => (string) $c,
212
- 'slug' => (string) $att['nicename'],
213
- 'domain' => (string) $att['domain']
214
- );
215
- }
216
-
217
- foreach ( $wp->postmeta as $meta ) {
218
- $post['postmeta'][] = array(
219
- 'key' => (string) $meta->meta_key,
220
- 'value' => (string) $meta->meta_value
221
- );
222
- }
223
-
224
- foreach ( $wp->comment as $comment ) {
225
- $meta = array();
226
- if ( isset( $comment->commentmeta ) ) {
227
- foreach ( $comment->commentmeta as $m ) {
228
- $meta[] = array(
229
- 'key' => (string) $m->meta_key,
230
- 'value' => (string) $m->meta_value
231
- );
232
- }
233
- }
234
-
235
- $post['comments'][] = array(
236
- 'comment_id' => (int) $comment->comment_id,
237
- 'comment_author' => (string) $comment->comment_author,
238
- 'comment_author_email' => (string) $comment->comment_author_email,
239
- 'comment_author_IP' => (string) $comment->comment_author_IP,
240
- 'comment_author_url' => (string) $comment->comment_author_url,
241
- 'comment_date' => (string) $comment->comment_date,
242
- 'comment_date_gmt' => (string) $comment->comment_date_gmt,
243
- 'comment_content' => (string) $comment->comment_content,
244
- 'comment_approved' => (string) $comment->comment_approved,
245
- 'comment_type' => (string) $comment->comment_type,
246
- 'comment_parent' => (string) $comment->comment_parent,
247
- 'comment_user_id' => (int) $comment->comment_user_id,
248
- 'commentmeta' => $meta,
249
- );
250
- }
251
-
252
- $posts[] = $post;
253
- }
254
-
255
- return array(
256
- 'authors' => $authors,
257
- 'posts' => $posts,
258
- 'categories' => $categories,
259
- 'tags' => $tags,
260
- 'terms' => $terms,
261
- 'base_url' => $base_url,
262
- 'version' => $wxr_version
263
- );
264
- }
265
- }
266
-
267
- /**
268
- * WXR Parser that makes use of the XML Parser PHP extension.
269
- */
270
- class WXR_Parser_XML {
271
- var $wp_tags = array(
272
- 'wp:post_id', 'wp:post_date', 'wp:post_date_gmt', 'wp:comment_status', 'wp:ping_status', 'wp:attachment_url',
273
- 'wp:status', 'wp:post_name', 'wp:post_parent', 'wp:menu_order', 'wp:post_type', 'wp:post_password',
274
- 'wp:is_sticky', 'wp:term_id', 'wp:category_nicename', 'wp:category_parent', 'wp:cat_name', 'wp:category_description',
275
- 'wp:tag_slug', 'wp:tag_name', 'wp:tag_description', 'wp:term_taxonomy', 'wp:term_parent',
276
- 'wp:term_name', 'wp:term_description', 'wp:author_id', 'wp:author_login', 'wp:author_email', 'wp:author_display_name',
277
- 'wp:author_first_name', 'wp:author_last_name',
278
- );
279
- var $wp_sub_tags = array(
280
- 'wp:comment_id', 'wp:comment_author', 'wp:comment_author_email', 'wp:comment_author_url',
281
- 'wp:comment_author_IP', 'wp:comment_date', 'wp:comment_date_gmt', 'wp:comment_content',
282
- 'wp:comment_approved', 'wp:comment_type', 'wp:comment_parent', 'wp:comment_user_id',
283
- );
284
-
285
- function parse( $file ) {
286
- $this->wxr_version = $this->in_post = $this->cdata = $this->data = $this->sub_data = $this->in_tag = $this->in_sub_tag = false;
287
- $this->authors = $this->posts = $this->term = $this->category = $this->tag = array();
288
-
289
- $xml = xml_parser_create( 'UTF-8' );
290
- xml_parser_set_option( $xml, XML_OPTION_SKIP_WHITE, 1 );
291
- xml_parser_set_option( $xml, XML_OPTION_CASE_FOLDING, 0 );
292
- xml_set_object( $xml, $this );
293
- xml_set_character_data_handler( $xml, 'cdata' );
294
- xml_set_element_handler( $xml, 'tag_open', 'tag_close' );
295
-
296
- if ( ! xml_parse( $xml, file_get_contents( $file ), true ) ) {
297
- $current_line = xml_get_current_line_number( $xml );
298
- $current_column = xml_get_current_column_number( $xml );
299
- $error_code = xml_get_error_code( $xml );
300
- $error_string = xml_error_string( $error_code );
301
- return new WP_Error( 'XML_parse_error', 'There was an error when reading this WXR file', array( $current_line, $current_column, $error_string ) );
302
- }
303
- xml_parser_free( $xml );
304
-
305
- if ( ! preg_match( '/^\d+\.\d+$/', $this->wxr_version ) )
306
- return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
307
-
308
- return array(
309
- 'authors' => $this->authors,
310
- 'posts' => $this->posts,
311
- 'categories' => $this->category,
312
- 'tags' => $this->tag,
313
- 'terms' => $this->term,
314
- 'base_url' => $this->base_url,
315
- 'version' => $this->wxr_version
316
- );
317
- }
318
-
319
- function tag_open( $parse, $tag, $attr ) {
320
- if ( in_array( $tag, $this->wp_tags ) ) {
321
- $this->in_tag = substr( $tag, 3 );
322
- return;
323
- }
324
-
325
- if ( in_array( $tag, $this->wp_sub_tags ) ) {
326
- $this->in_sub_tag = substr( $tag, 3 );
327
- return;
328
- }
329
-
330
- switch ( $tag ) {
331
- case 'category':
332
- if ( isset($attr['domain'], $attr['nicename']) ) {
333
- $this->sub_data['domain'] = $attr['domain'];
334
- $this->sub_data['slug'] = $attr['nicename'];
335
- }
336
- break;
337
- case 'item': $this->in_post = true;
338
- case 'title': if ( $this->in_post ) $this->in_tag = 'post_title'; break;
339
- case 'guid': $this->in_tag = 'guid'; break;
340
- case 'dc:creator': $this->in_tag = 'post_author'; break;
341
- case 'content:encoded': $this->in_tag = 'post_content'; break;
342
- case 'excerpt:encoded': $this->in_tag = 'post_excerpt'; break;
343
-
344
- case 'wp:term_slug': $this->in_tag = 'slug'; break;
345
- case 'wp:meta_key': $this->in_sub_tag = 'key'; break;
346
- case 'wp:meta_value': $this->in_sub_tag = 'value'; break;
347
- }
348
- }
349
-
350
- function cdata( $parser, $cdata ) {
351
- if ( ! trim( $cdata ) )
352
- return;
353
-
354
- if ( false !== $this->in_tag || false !== $this->in_sub_tag ) {
355
- $this->cdata .= $cdata;
356
- } else {
357
- $this->cdata .= trim( $cdata );
358
- }
359
- }
360
-
361
- function tag_close( $parser, $tag ) {
362
- switch ( $tag ) {
363
- case 'wp:comment':
364
- unset( $this->sub_data['key'], $this->sub_data['value'] ); // remove meta sub_data
365
- if ( ! empty( $this->sub_data ) )
366
- $this->data['comments'][] = $this->sub_data;
367
- $this->sub_data = false;
368
- break;
369
- case 'wp:commentmeta':
370
- $this->sub_data['commentmeta'][] = array(
371
- 'key' => $this->sub_data['key'],
372
- 'value' => $this->sub_data['value']
373
- );
374
- break;
375
- case 'category':
376
- if ( ! empty( $this->sub_data ) ) {
377
- $this->sub_data['name'] = $this->cdata;
378
- $this->data['terms'][] = $this->sub_data;
379
- }
380
- $this->sub_data = false;
381
- break;
382
- case 'wp:postmeta':
383
- if ( ! empty( $this->sub_data ) )
384
- $this->data['postmeta'][] = $this->sub_data;
385
- $this->sub_data = false;
386
- break;
387
- case 'item':
388
- $this->posts[] = $this->data;
389
- $this->data = false;
390
- break;
391
- case 'wp:category':
392
- case 'wp:tag':
393
- case 'wp:term':
394
- $n = substr( $tag, 3 );
395
- array_push( $this->$n, $this->data );
396
- $this->data = false;
397
- break;
398
- case 'wp:author':
399
- if ( ! empty($this->data['author_login']) )
400
- $this->authors[$this->data['author_login']] = $this->data;
401
- $this->data = false;
402
- break;
403
- case 'wp:base_site_url':
404
- $this->base_url = $this->cdata;
405
- break;
406
- case 'wp:wxr_version':
407
- $this->wxr_version = $this->cdata;
408
- break;
409
-
410
- default:
411
- if ( $this->in_sub_tag ) {
412
- $this->sub_data[$this->in_sub_tag] = ! empty( $this->cdata ) ? $this->cdata : '';
413
- $this->in_sub_tag = false;
414
- } else if ( $this->in_tag ) {
415
- $this->data[$this->in_tag] = ! empty( $this->cdata ) ? $this->cdata : '';
416
- $this->in_tag = false;
417
- }
418
- }
419
-
420
- $this->cdata = false;
421
- }
422
- }
423
-
424
- /**
425
- * WXR Parser that uses regular expressions. Fallback for installs without an XML parser.
426
- */
427
- class WXR_Parser_Regex {
428
- var $authors = array();
429
- var $posts = array();
430
- var $categories = array();
431
- var $tags = array();
432
- var $terms = array();
433
- var $base_url = '';
434
-
435
- function __construct() {
436
- $this->has_gzip = is_callable( 'gzopen' );
437
- }
438
-
439
- function parse( $file ) {
440
- $wxr_version = $in_post = false;
441
-
442
- $fp = $this->fopen( $file, 'r' );
443
- if ( $fp ) {
444
- while ( ! $this->feof( $fp ) ) {
445
- $importline = rtrim( $this->fgets( $fp ) );
446
-
447
- if ( ! $wxr_version && preg_match( '|<wp:wxr_version>(\d+\.\d+)</wp:wxr_version>|', $importline, $version ) )
448
- $wxr_version = $version[1];
449
-
450
- if ( false !== strpos( $importline, '<wp:base_site_url>' ) ) {
451
- preg_match( '|<wp:base_site_url>(.*?)</wp:base_site_url>|is', $importline, $url );
452
- $this->base_url = $url[1];
453
- continue;
454
- }
455
- if ( false !== strpos( $importline, '<wp:category>' ) ) {
456
- preg_match( '|<wp:category>(.*?)</wp:category>|is', $importline, $category );
457
- $this->categories[] = $this->process_category( $category[1] );
458
- continue;
459
- }
460
- if ( false !== strpos( $importline, '<wp:tag>' ) ) {
461
- preg_match( '|<wp:tag>(.*?)</wp:tag>|is', $importline, $tag );
462
- $this->tags[] = $this->process_tag( $tag[1] );
463
- continue;
464
- }
465
- if ( false !== strpos( $importline, '<wp:term>' ) ) {
466
- preg_match( '|<wp:term>(.*?)</wp:term>|is', $importline, $term );
467
- $this->terms[] = $this->process_term( $term[1] );
468
- continue;
469
- }
470
- if ( false !== strpos( $importline, '<wp:author>' ) ) {
471
- preg_match( '|<wp:author>(.*?)</wp:author>|is', $importline, $author );
472
- $a = $this->process_author( $author[1] );
473
- $this->authors[$a['author_login']] = $a;
474
- continue;
475
- }
476
- if ( false !== strpos( $importline, '<item>' ) ) {
477
- $post = '';
478
- $in_post = true;
479
- continue;
480
- }
481
- if ( false !== strpos( $importline, '</item>' ) ) {
482
- $in_post = false;
483
- $this->posts[] = $this->process_post( $post );
484
- continue;
485
- }
486
- if ( $in_post ) {
487
- $post .= $importline . "\n";
488
- }
489
- }
490
-
491
- $this->fclose($fp);
492
- }
493
-
494
- if ( ! $wxr_version )
495
- return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) );
496
-
497
- return array(
498
- 'authors' => $this->authors,
499
- 'posts' => $this->posts,
500
- 'categories' => $this->categories,
501
- 'tags' => $this->tags,
502
- 'terms' => $this->terms,
503
- 'base_url' => $this->base_url,
504
- 'version' => $wxr_version
505
- );
506
- }
507
-
508
- function get_tag( $string, $tag ) {
509
- preg_match( "|<$tag.*?>(.*?)</$tag>|is", $string, $return );
510
- if ( isset( $return[1] ) ) {
511
- if ( substr( $return[1], 0, 9 ) == '<![CDATA[' ) {
512
- if ( strpos( $return[1], ']]]]><![CDATA[>' ) !== false ) {
513
- preg_match_all( '|<!\[CDATA\[(.*?)\]\]>|s', $return[1], $matches );
514
- $return = '';
515
- foreach( $matches[1] as $match )
516
- $return .= $match;
517
- } else {
518
- $return = preg_replace( '|^<!\[CDATA\[(.*)\]\]>$|s', '$1', $return[1] );
519
- }
520
- } else {
521
- $return = $return[1];
522
- }
523
- } else {
524
- $return = '';
525
- }
526
- return $return;
527
- }
528
-
529
- function process_category( $c ) {
530
- return array(
531
- 'term_id' => $this->get_tag( $c, 'wp:term_id' ),
532
- 'cat_name' => $this->get_tag( $c, 'wp:cat_name' ),
533
- 'category_nicename' => $this->get_tag( $c, 'wp:category_nicename' ),
534
- 'category_parent' => $this->get_tag( $c, 'wp:category_parent' ),
535
- 'category_description' => $this->get_tag( $c, 'wp:category_description' ),
536
- );
537
- }
538
-
539
- function process_tag( $t ) {
540
- return array(
541
- 'term_id' => $this->get_tag( $t, 'wp:term_id' ),
542
- 'tag_name' => $this->get_tag( $t, 'wp:tag_name' ),
543
- 'tag_slug' => $this->get_tag( $t, 'wp:tag_slug' ),
544
- 'tag_description' => $this->get_tag( $t, 'wp:tag_description' ),
545
- );
546
- }
547
-
548
- function process_term( $t ) {
549
- return array(
550
- 'term_id' => $this->get_tag( $t, 'wp:term_id' ),
551
- 'term_taxonomy' => $this->get_tag( $t, 'wp:term_taxonomy' ),
552
- 'slug' => $this->get_tag( $t, 'wp:term_slug' ),
553
- 'term_parent' => $this->get_tag( $t, 'wp:term_parent' ),
554
- 'term_name' => $this->get_tag( $t, 'wp:term_name' ),
555
- 'term_description' => $this->get_tag( $t, 'wp:term_description' ),
556
- );
557
- }
558
-
559
- function process_author( $a ) {
560
- return array(
561
- 'author_id' => $this->get_tag( $a, 'wp:author_id' ),
562
- 'author_login' => $this->get_tag( $a, 'wp:author_login' ),
563
- 'author_email' => $this->get_tag( $a, 'wp:author_email' ),
564
- 'author_display_name' => $this->get_tag( $a, 'wp:author_display_name' ),
565
- 'author_first_name' => $this->get_tag( $a, 'wp:author_first_name' ),
566
- 'author_last_name' => $this->get_tag( $a, 'wp:author_last_name' ),
567
- );
568
- }
569
-
570
- function process_post( $post ) {
571
- $post_id = $this->get_tag( $post, 'wp:post_id' );
572
- $post_title = $this->get_tag( $post, 'title' );
573
- $post_date = $this->get_tag( $post, 'wp:post_date' );
574
- $post_date_gmt = $this->get_tag( $post, 'wp:post_date_gmt' );
575
- $comment_status = $this->get_tag( $post, 'wp:comment_status' );
576
- $ping_status = $this->get_tag( $post, 'wp:ping_status' );
577
- $status = $this->get_tag( $post, 'wp:status' );
578
- $post_name = $this->get_tag( $post, 'wp:post_name' );
579
- $post_parent = $this->get_tag( $post, 'wp:post_parent' );
580
- $menu_order = $this->get_tag( $post, 'wp:menu_order' );
581
- $post_type = $this->get_tag( $post, 'wp:post_type' );
582
- $post_password = $this->get_tag( $post, 'wp:post_password' );
583
- $is_sticky = $this->get_tag( $post, 'wp:is_sticky' );
584
- $guid = $this->get_tag( $post, 'guid' );
585
- $post_author = $this->get_tag( $post, 'dc:creator' );
586
-
587
- $post_excerpt = $this->get_tag( $post, 'excerpt:encoded' );
588
- $post_excerpt = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_excerpt );
589
- $post_excerpt = str_replace( '<br>', '<br />', $post_excerpt );
590
- $post_excerpt = str_replace( '<hr>', '<hr />', $post_excerpt );
591
-
592
- $post_content = $this->get_tag( $post, 'content:encoded' );
593
- $post_content = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_content );
594
- $post_content = str_replace( '<br>', '<br />', $post_content );
595
- $post_content = str_replace( '<hr>', '<hr />', $post_content );
596
-
597
- $postdata = compact( 'post_id', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_excerpt',
598
- 'post_title', 'status', 'post_name', 'comment_status', 'ping_status', 'guid', 'post_parent',
599
- 'menu_order', 'post_type', 'post_password', 'is_sticky'
600
- );
601
-
602
- $attachment_url = $this->get_tag( $post, 'wp:attachment_url' );
603
- if ( $attachment_url )
604
- $postdata['attachment_url'] = $attachment_url;
605
-
606
- preg_match_all( '|<category domain="([^"]+?)" nicename="([^"]+?)">(.+?)</category>|is', $post, $terms, PREG_SET_ORDER );
607
- foreach ( $terms as $t ) {
608
- $post_terms[] = array(
609
- 'slug' => $t[2],
610
- 'domain' => $t[1],
611
- 'name' => str_replace( array( '<![CDATA[', ']]>' ), '', $t[3] ),
612
- );
613
- }
614
- if ( ! empty( $post_terms ) ) $postdata['terms'] = $post_terms;
615
-
616
- preg_match_all( '|<wp:comment>(.+?)</wp:comment>|is', $post, $comments );
617
- $comments = $comments[1];
618
- if ( $comments ) {
619
- foreach ( $comments as $comment ) {
620
- preg_match_all( '|<wp:commentmeta>(.+?)</wp:commentmeta>|is', $comment, $commentmeta );
621
- $commentmeta = $commentmeta[1];
622
- $c_meta = array();
623
- foreach ( $commentmeta as $m ) {
624
- $c_meta[] = array(
625
- 'key' => $this->get_tag( $m, 'wp:meta_key' ),
626
- 'value' => $this->get_tag( $m, 'wp:meta_value' ),
627
- );
628
- }
629
-
630
- $post_comments[] = array(
631
- 'comment_id' => $this->get_tag( $comment, 'wp:comment_id' ),
632
- 'comment_author' => $this->get_tag( $comment, 'wp:comment_author' ),
633
- 'comment_author_email' => $this->get_tag( $comment, 'wp:comment_author_email' ),
634
- 'comment_author_IP' => $this->get_tag( $comment, 'wp:comment_author_IP' ),
635
- 'comment_author_url' => $this->get_tag( $comment, 'wp:comment_author_url' ),
636
- 'comment_date' => $this->get_tag( $comment, 'wp:comment_date' ),
637
- 'comment_date_gmt' => $this->get_tag( $comment, 'wp:comment_date_gmt' ),
638
- 'comment_content' => $this->get_tag( $comment, 'wp:comment_content' ),
639
- 'comment_approved' => $this->get_tag( $comment, 'wp:comment_approved' ),
640
- 'comment_type' => $this->get_tag( $comment, 'wp:comment_type' ),
641
- 'comment_parent' => $this->get_tag( $comment, 'wp:comment_parent' ),
642
- 'comment_user_id' => $this->get_tag( $comment, 'wp:comment_user_id' ),
643
- 'commentmeta' => $c_meta,
644
- );
645
- }
646
- }
647
- if ( ! empty( $post_comments ) ) $postdata['comments'] = $post_comments;
648
-
649
- preg_match_all( '|<wp:postmeta>(.+?)</wp:postmeta>|is', $post, $postmeta );
650
- $postmeta = $postmeta[1];
651
- if ( $postmeta ) {
652
- foreach ( $postmeta as $p ) {
653
- $post_postmeta[] = array(
654
- 'key' => $this->get_tag( $p, 'wp:meta_key' ),
655
- 'value' => $this->get_tag( $p, 'wp:meta_value' ),
656
- );
657
- }
658
- }
659
- if ( ! empty( $post_postmeta ) ) $postdata['postmeta'] = $post_postmeta;
660
-
661
- return $postdata;
662
- }
663
-
664
- function _normalize_tag( $matches ) {
665
- return '<' . strtolower( $matches[1] );
666
- }
667
-
668
- function fopen( $filename, $mode = 'r' ) {
669
- if ( $this->has_gzip )
670
- return gzopen( $filename, $mode );
671
- return fopen( $filename, $mode );
672
- }
673
-
674
- function feof( $fp ) {
675
- if ( $this->has_gzip )
676
- return gzeof( $fp );
677
- return feof( $fp );
678
- }
679
-
680
- function fgets( $fp, $len = 8192 ) {
681
- if ( $this->has_gzip )
682
- return gzgets( $fp, $len );
683
- return fgets( $fp, $len );
684
- }
685
-
686
- function fclose( $fp ) {
687
- if ( $this->has_gzip )
688
- return gzclose( $fp );
689
- return fclose( $fp );
690
- }
691
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/theme-supports.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ add_action( 'tgmpa_register', 'demo_contents_register_required_plugins' );
4
+
5
+ function demo_contents_register_required_plugins() {
6
+
7
+ $template_slug = get_option( 'template' );
8
+ if ( in_array( $template_slug, array( 'onepress' , 'screenr' ) ) ) {
9
+ /*
10
+ * Array of plugin arrays. Required keys are name and slug.
11
+ * If the source is NOT from the .org repo, then source is also required.
12
+ */
13
+ $plugins = array(
14
+
15
+ // This is an example of how to include a plugin from the WordPress Plugin Repository.
16
+ array(
17
+ 'name' => 'Contact Form 7',
18
+ 'slug' => 'contact-form-7',
19
+ 'required' => false,
20
+ ),
21
+
22
+ );
23
+ } else {
24
+ return ;
25
+ }
26
+
27
+ /*
28
+ * Array of configuration settings. Amend each line as needed.
29
+ *
30
+ * TGMPA will start providing localized text strings soon. If you already have translations of our standard
31
+ * strings available, please help us make TGMPA even better by giving us access to these translations or by
32
+ * sending in a pull-request with .po file(s) with the translations.
33
+ *
34
+ * Only uncomment the strings in the config array if you want to customize the strings.
35
+ */
36
+ $config = array(
37
+ 'id' => 'demo-contents', // Unique ID for hashing notices for multiple instances of TGMPA.
38
+ 'default_path' => '', // Default absolute path to bundled plugins.
39
+ 'menu' => 'tgmpa-install-plugins', // Menu slug.
40
+ 'parent_slug' => 'plugins.php', // Parent menu slug.
41
+ 'capability' => 'manage_options', // Capability needed to view plugin install page, should be a capability associated with the parent menu used.
42
+ 'has_notices' => true, // Show admin notices or not.
43
+ 'dismissable' => true, // If false, a user cannot dismiss the nag message.
44
+ 'dismiss_msg' => '', // If 'dismissable' is false, this message will be output at top of nag.
45
+ 'is_automatic' => false, // Automatically activate plugins after installation or not.
46
+ 'message' => '', // Message to output right before the plugins table.
47
+
48
+ );
49
+
50
+ tgmpa( $plugins, $config );
51
+ }
inc/wordpress-importer.php DELETED
@@ -1,1215 +0,0 @@
1
- <?php
2
- /*
3
- Plugin URI: http://wordpress.org/extend/plugins/wordpress-importer/
4
- Description: Import posts, pages, comments, custom fields, categories, tags and more from a WordPress export file.
5
- Author: wordpressdotorg
6
- Author URI: http://wordpress.org/
7
- Version: 0.6.3
8
- Text Domain: wordpress-importer
9
- License: GPL version 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10
- */
11
-
12
-
13
- if ( ! defined( 'WP_LOAD_IMPORTERS' ) )
14
- return;
15
-
16
- /** Display verbose errors */
17
- define( 'IMPORT_DEBUG', true );
18
-
19
- // Load Importer API
20
- require_once ABSPATH . 'wp-admin/includes/import.php';
21
-
22
- if ( ! class_exists( 'WP_Importer' ) ) {
23
- $class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
24
- if ( file_exists( $class_wp_importer ) )
25
- require $class_wp_importer;
26
- }
27
-
28
- // include WXR file parsers
29
- require dirname( __FILE__ ) . '/parsers.php';
30
-
31
- /**
32
- * WordPress Importer class for managing the import process of a WXR file
33
- *
34
- * @package WordPress
35
- * @subpackage Importer
36
- */
37
- if ( class_exists( 'WP_Importer' ) ) {
38
- class WP_Import extends WP_Importer {
39
- var $max_wxr_version = 1.2; // max. supported WXR version
40
-
41
- var $id; // WXR attachment ID
42
-
43
- // information to import from WXR file
44
- var $version;
45
- var $authors = array();
46
- var $posts = array();
47
- var $terms = array();
48
- var $categories = array();
49
- var $tags = array();
50
- var $base_url = '';
51
-
52
- // mappings from old information to new
53
- var $processed_authors = array();
54
- var $author_mapping = array();
55
- var $processed_terms = array();
56
- var $processed_posts = array();
57
- var $post_orphans = array();
58
- var $processed_menu_items = array();
59
- var $menu_item_orphans = array();
60
- var $missing_menu_items = array();
61
-
62
- var $fetch_attachments = false;
63
- var $url_remap = array();
64
- var $featured_images = array();
65
-
66
- /**
67
- * Registered callback function for the WordPress Importer
68
- *
69
- * Manages the three separate stages of the WXR import process
70
- */
71
- function dispatch() {
72
- $this->header();
73
-
74
- $step = empty( $_GET['step'] ) ? 0 : (int) $_GET['step'];
75
- switch ( $step ) {
76
- case 0:
77
- $this->greet();
78
- break;
79
- case 1:
80
- check_admin_referer( 'import-upload' );
81
- if ( $this->handle_upload() )
82
- $this->import_options();
83
- break;
84
- case 2:
85
- check_admin_referer( 'import-wordpress' );
86
- $this->fetch_attachments = ( ! empty( $_POST['fetch_attachments'] ) && $this->allow_fetch_attachments() );
87
- $this->id = (int) $_POST['import_id'];
88
- $file = get_attached_file( $this->id );
89
- set_time_limit(0);
90
- $this->import( $file );
91
- break;
92
- }
93
-
94
- $this->footer();
95
- }
96
-
97
- /**
98
- * The main controller for the actual import stage.
99
- *
100
- * @param string $file Path to the WXR file for importing
101
- */
102
- function import( $file ) {
103
- add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
104
- add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
105
-
106
- $this->import_start( $file );
107
-
108
- $this->get_author_mapping();
109
-
110
- wp_suspend_cache_invalidation( true );
111
- $this->process_categories();
112
- $this->process_tags();
113
- $this->process_terms();
114
- $this->process_posts();
115
- wp_suspend_cache_invalidation( false );
116
-
117
- // update incorrect/missing information in the DB
118
- $this->backfill_parents();
119
- $this->backfill_attachment_urls();
120
- $this->remap_featured_images();
121
-
122
- $this->import_end();
123
- }
124
-
125
- /**
126
- * Parses the WXR file and prepares us for the task of processing parsed data
127
- *
128
- * @param string $file Path to the WXR file for importing
129
- */
130
- function import_start( $file ) {
131
- if ( ! is_file($file) ) {
132
- echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
133
- echo __( 'The file does not exist, please try again.', 'wordpress-importer' ) . '</p>';
134
- $this->footer();
135
- die();
136
- }
137
-
138
- $import_data = $this->parse( $file );
139
-
140
- if ( is_wp_error( $import_data ) ) {
141
- echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
142
- echo esc_html( $import_data->get_error_message() ) . '</p>';
143
- $this->footer();
144
- die();
145
- }
146
-
147
- $this->version = $import_data['version'];
148
- $this->get_authors_from_import( $import_data );
149
- $this->posts = $import_data['posts'];
150
- $this->terms = $import_data['terms'];
151
- $this->categories = $import_data['categories'];
152
- $this->tags = $import_data['tags'];
153
- $this->base_url = esc_url( $import_data['base_url'] );
154
-
155
- wp_defer_term_counting( true );
156
- wp_defer_comment_counting( true );
157
-
158
- do_action( 'import_start' );
159
- }
160
-
161
- /**
162
- * Performs post-import cleanup of files and the cache
163
- */
164
- function import_end() {
165
- wp_import_cleanup( $this->id );
166
-
167
- wp_cache_flush();
168
- foreach ( get_taxonomies() as $tax ) {
169
- delete_option( "{$tax}_children" );
170
- _get_term_hierarchy( $tax );
171
- }
172
-
173
- wp_defer_term_counting( false );
174
- wp_defer_comment_counting( false );
175
-
176
- echo '<p>' . __( 'All done.', 'wordpress-importer' ) . ' <a href="' . admin_url() . '">' . __( 'Have fun!', 'wordpress-importer' ) . '</a>' . '</p>';
177
- echo '<p>' . __( 'Remember to update the passwords and roles of imported users.', 'wordpress-importer' ) . '</p>';
178
-
179
- do_action( 'import_end' );
180
- }
181
-
182
- /**
183
- * Handles the WXR upload and initial parsing of the file to prepare for
184
- * displaying author import options
185
- *
186
- * @return bool False if error uploading or invalid file, true otherwise
187
- */
188
- function handle_upload() {
189
- $file = wp_import_handle_upload();
190
-
191
- if ( isset( $file['error'] ) ) {
192
- echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
193
- echo esc_html( $file['error'] ) . '</p>';
194
- return false;
195
- } else if ( ! file_exists( $file['file'] ) ) {
196
- echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
197
- printf( __( 'The export file could not be found at <code>%s</code>. It is likely that this was caused by a permissions problem.', 'wordpress-importer' ), esc_html( $file['file'] ) );
198
- echo '</p>';
199
- return false;
200
- }
201
-
202
- $this->id = (int) $file['id'];
203
- $import_data = $this->parse( $file['file'] );
204
- if ( is_wp_error( $import_data ) ) {
205
- echo '<p><strong>' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '</strong><br />';
206
- echo esc_html( $import_data->get_error_message() ) . '</p>';
207
- return false;
208
- }
209
-
210
- $this->version = $import_data['version'];
211
- if ( $this->version > $this->max_wxr_version ) {
212
- echo '<div class="error"><p><strong>';
213
- printf( __( 'This WXR file (version %s) may not be supported by this version of the importer. Please consider updating.', 'wordpress-importer' ), esc_html($import_data['version']) );
214
- echo '</strong></p></div>';
215
- }
216
-
217
- $this->get_authors_from_import( $import_data );
218
-
219
- return true;
220
- }
221
-
222
- /**
223
- * Retrieve authors from parsed WXR data
224
- *
225
- * Uses the provided author information from WXR 1.1 files
226
- * or extracts info from each post for WXR 1.0 files
227
- *
228
- * @param array $import_data Data returned by a WXR parser
229
- */
230
- function get_authors_from_import( $import_data ) {
231
- if ( ! empty( $import_data['authors'] ) ) {
232
- $this->authors = $import_data['authors'];
233
- // no author information, grab it from the posts
234
- } else {
235
- foreach ( $import_data['posts'] as $post ) {
236
- $login = sanitize_user( $post['post_author'], true );
237
- if ( empty( $login ) ) {
238
- printf( __( 'Failed to import author %s. Their posts will be attributed to the current user.', 'wordpress-importer' ), esc_html( $post['post_author'] ) );
239
- echo '<br />';
240
- continue;
241
- }
242
-
243
- if ( ! isset($this->authors[$login]) )
244
- $this->authors[$login] = array(
245
- 'author_login' => $login,
246
- 'author_display_name' => $post['post_author']
247
- );
248
- }
249
- }
250
- }
251
-
252
- /**
253
- * Display pre-import options, author importing/mapping and option to
254
- * fetch attachments
255
- */
256
- function import_options() {
257
- $j = 0;
258
- ?>
259
- <form action="<?php echo admin_url( 'admin.php?import=wordpress&amp;step=2' ); ?>" method="post">
260
- <?php wp_nonce_field( 'import-wordpress' ); ?>
261
- <input type="hidden" name="import_id" value="<?php echo $this->id; ?>" />
262
-
263
- <?php if ( ! empty( $this->authors ) ) : ?>
264
- <h3><?php _e( 'Assign Authors', 'wordpress-importer' ); ?></h3>
265
- <p><?php _e( 'To make it easier for you to edit and save the imported content, you may want to reassign the author of the imported item to an existing user of this site. For example, you may want to import all the entries as <code>admin</code>s entries.', 'wordpress-importer' ); ?></p>
266
- <?php if ( $this->allow_create_users() ) : ?>
267
- <p><?php printf( __( 'If a new user is created by WordPress, a new password will be randomly generated and the new user&#8217;s role will be set as %s. Manually changing the new user&#8217;s details will be necessary.', 'wordpress-importer' ), esc_html( get_option('default_role') ) ); ?></p>
268
- <?php endif; ?>
269
- <ol id="authors">
270
- <?php foreach ( $this->authors as $author ) : ?>
271
- <li><?php $this->author_select( $j++, $author ); ?></li>
272
- <?php endforeach; ?>
273
- </ol>
274
- <?php endif; ?>
275
-
276
- <?php if ( $this->allow_fetch_attachments() ) : ?>
277
- <h3><?php _e( 'Import Attachments', 'wordpress-importer' ); ?></h3>
278
- <p>
279
- <input type="checkbox" value="1" name="fetch_attachments" id="import-attachments" />
280
- <label for="import-attachments"><?php _e( 'Download and import file attachments', 'wordpress-importer' ); ?></label>
281
- </p>
282
- <?php endif; ?>
283
-
284
- <p class="submit"><input type="submit" class="button" value="<?php esc_attr_e( 'Submit', 'wordpress-importer' ); ?>" /></p>
285
- </form>
286
- <?php
287
- }
288
-
289
- /**
290
- * Display import options for an individual author. That is, either create
291
- * a new user based on import info or map to an existing user
292
- *
293
- * @param int $n Index for each author in the form
294
- * @param array $author Author information, e.g. login, display name, email
295
- */
296
- function author_select( $n, $author ) {
297
- _e( 'Import author:', 'wordpress-importer' );
298
- echo ' <strong>' . esc_html( $author['author_display_name'] );
299
- if ( $this->version != '1.0' ) echo ' (' . esc_html( $author['author_login'] ) . ')';
300
- echo '</strong><br />';
301
-
302
- if ( $this->version != '1.0' )
303
- echo '<div style="margin-left:18px">';
304
-
305
- $create_users = $this->allow_create_users();
306
- if ( $create_users ) {
307
- if ( $this->version != '1.0' ) {
308
- _e( 'or create new user with login name:', 'wordpress-importer' );
309
- $value = '';
310
- } else {
311
- _e( 'as a new user:', 'wordpress-importer' );
312
- $value = esc_attr( sanitize_user( $author['author_login'], true ) );
313
- }
314
-
315
- echo ' <input type="text" name="user_new['.$n.']" value="'. $value .'" /><br />';
316
- }
317
-
318
- if ( ! $create_users && $this->version == '1.0' )
319
- _e( 'assign posts to an existing user:', 'wordpress-importer' );
320
- else
321
- _e( 'or assign posts to an existing user:', 'wordpress-importer' );
322
- wp_dropdown_users( array( 'name' => "user_map[$n]", 'multi' => true, 'show_option_all' => __( '- Select -', 'wordpress-importer' ) ) );
323
- echo '<input type="hidden" name="imported_authors['.$n.']" value="' . esc_attr( $author['author_login'] ) . '" />';
324
-
325
- if ( $this->version != '1.0' )
326
- echo '</div>';
327
- }
328
-
329
- /**
330
- * Map old author logins to local user IDs based on decisions made
331
- * in import options form. Can map to an existing user, create a new user
332
- * or falls back to the current user in case of error with either of the previous
333
- */
334
- function get_author_mapping() {
335
- if ( ! isset( $_POST['imported_authors'] ) )
336
- return;
337
-
338
- $create_users = $this->allow_create_users();
339
-
340
- foreach ( (array) $_POST['imported_authors'] as $i => $old_login ) {
341
- // Multisite adds strtolower to sanitize_user. Need to sanitize here to stop breakage in process_posts.
342
- $santized_old_login = sanitize_user( $old_login, true );
343
- $old_id = isset( $this->authors[$old_login]['author_id'] ) ? intval($this->authors[$old_login]['author_id']) : false;
344
-
345
- if ( ! empty( $_POST['user_map'][$i] ) ) {
346
- $user = get_userdata( intval($_POST['user_map'][$i]) );
347
- if ( isset( $user->ID ) ) {
348
- if ( $old_id )
349
- $this->processed_authors[$old_id] = $user->ID;
350
- $this->author_mapping[$santized_old_login] = $user->ID;
351
- }
352
- } else if ( $create_users ) {
353
- if ( ! empty($_POST['user_new'][$i]) ) {
354
- $user_id = wp_create_user( $_POST['user_new'][$i], wp_generate_password() );
355
- } else if ( $this->version != '1.0' ) {
356
- $user_data = array(
357
- 'user_login' => $old_login,
358
- 'user_pass' => wp_generate_password(),
359
- 'user_email' => isset( $this->authors[$old_login]['author_email'] ) ? $this->authors[$old_login]['author_email'] : '',
360
- 'display_name' => $this->authors[$old_login]['author_display_name'],
361
- 'first_name' => isset( $this->authors[$old_login]['author_first_name'] ) ? $this->authors[$old_login]['author_first_name'] : '',
362
- 'last_name' => isset( $this->authors[$old_login]['author_last_name'] ) ? $this->authors[$old_login]['author_last_name'] : '',
363
- );
364
- $user_id = wp_insert_user( $user_data );
365
- }
366
-
367
- if ( ! is_wp_error( $user_id ) ) {
368
- if ( $old_id )
369
- $this->processed_authors[$old_id] = $user_id;
370
- $this->author_mapping[$santized_old_login] = $user_id;
371
- } else {
372
- printf( __( 'Failed to create new user for %s. Their posts will be attributed to the current user.', 'wordpress-importer' ), esc_html($this->authors[$old_login]['author_display_name']) );
373
- if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
374
- echo ' ' . $user_id->get_error_message();
375
- echo '<br />';
376
- }
377
- }
378
-
379
- // failsafe: if the user_id was invalid, default to the current user
380
- if ( ! isset( $this->author_mapping[$santized_old_login] ) ) {
381
- if ( $old_id )
382
- $this->processed_authors[$old_id] = (int) get_current_user_id();
383
- $this->author_mapping[$santized_old_login] = (int) get_current_user_id();
384
- }
385
- }
386
- }
387
-
388
- /**
389
- * Create new categories based on import information
390
- *
391
- * Doesn't create a new category if its slug already exists
392
- */
393
- function process_categories() {
394
- $this->categories = apply_filters( 'wp_import_categories', $this->categories );
395
-
396
- if ( empty( $this->categories ) )
397
- return;
398
-
399
- foreach ( $this->categories as $cat ) {
400
- // if the category already exists leave it alone
401
- $term_id = term_exists( $cat['category_nicename'], 'category' );
402
- if ( $term_id ) {
403
- if ( is_array($term_id) ) $term_id = $term_id['term_id'];
404
- if ( isset($cat['term_id']) )
405
- $this->processed_terms[intval($cat['term_id'])] = (int) $term_id;
406
- continue;
407
- }
408
-
409
- $category_parent = empty( $cat['category_parent'] ) ? 0 : category_exists( $cat['category_parent'] );
410
- $category_description = isset( $cat['category_description'] ) ? $cat['category_description'] : '';
411
- $catarr = array(
412
- 'category_nicename' => $cat['category_nicename'],
413
- 'category_parent' => $category_parent,
414
- 'cat_name' => $cat['cat_name'],
415
- 'category_description' => $category_description
416
- );
417
- $catarr = wp_slash( $catarr );
418
-
419
- $id = wp_insert_category( $catarr );
420
- if ( ! is_wp_error( $id ) ) {
421
- if ( isset($cat['term_id']) )
422
- $this->processed_terms[intval($cat['term_id'])] = $id;
423
- } else {
424
- printf( __( 'Failed to import category %s', 'wordpress-importer' ), esc_html($cat['category_nicename']) );
425
- if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
426
- echo ': ' . $id->get_error_message();
427
- echo '<br />';
428
- continue;
429
- }
430
-
431
- $this->process_termmeta( $cat, $id['term_id'] );
432
- }
433
-
434
- unset( $this->categories );
435
- }
436
-
437
- /**
438
- * Create new post tags based on import information
439
- *
440
- * Doesn't create a tag if its slug already exists
441
- */
442
- function process_tags() {
443
- $this->tags = apply_filters( 'wp_import_tags', $this->tags );
444
-
445
- if ( empty( $this->tags ) )
446
- return;
447
-
448
- foreach ( $this->tags as $tag ) {
449
- // if the tag already exists leave it alone
450
- $term_id = term_exists( $tag['tag_slug'], 'post_tag' );
451
- if ( $term_id ) {
452
- if ( is_array($term_id) ) $term_id = $term_id['term_id'];
453
- if ( isset($tag['term_id']) )
454
- $this->processed_terms[intval($tag['term_id'])] = (int) $term_id;
455
- continue;
456
- }
457
-
458
- $tag = wp_slash( $tag );
459
- $tag_desc = isset( $tag['tag_description'] ) ? $tag['tag_description'] : '';
460
- $tagarr = array( 'slug' => $tag['tag_slug'], 'description' => $tag_desc );
461
-
462
- $id = wp_insert_term( $tag['tag_name'], 'post_tag', $tagarr );
463
- if ( ! is_wp_error( $id ) ) {
464
- if ( isset($tag['term_id']) )
465
- $this->processed_terms[intval($tag['term_id'])] = $id['term_id'];
466
- } else {
467
- printf( __( 'Failed to import post tag %s', 'wordpress-importer' ), esc_html($tag['tag_name']) );
468
- if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
469
- echo ': ' . $id->get_error_message();
470
- echo '<br />';
471
- continue;
472
- }
473
-
474
- $this->process_termmeta( $tag, $id['term_id'] );
475
- }
476
-
477
- unset( $this->tags );
478
- }
479
-
480
- /**
481
- * Create new terms based on import information
482
- *
483
- * Doesn't create a term its slug already exists
484
- */
485
- function process_terms() {
486
- $this->terms = apply_filters( 'wp_import_terms', $this->terms );
487
-
488
- if ( empty( $this->terms ) )
489
- return;
490
-
491
- foreach ( $this->terms as $term ) {
492
- // if the term already exists in the correct taxonomy leave it alone
493
- $term_id = term_exists( $term['slug'], $term['term_taxonomy'] );
494
- if ( $term_id ) {
495
- if ( is_array($term_id) ) $term_id = $term_id['term_id'];
496
- if ( isset($term['term_id']) )
497
- $this->processed_terms[intval($term['term_id'])] = (int) $term_id;
498
- continue;
499
- }
500
-
501
- if ( empty( $term['term_parent'] ) ) {
502
- $parent = 0;
503
- } else {
504
- $parent = term_exists( $term['term_parent'], $term['term_taxonomy'] );
505
- if ( is_array( $parent ) ) $parent = $parent['term_id'];
506
- }
507
- $term = wp_slash( $term );
508
- $description = isset( $term['term_description'] ) ? $term['term_description'] : '';
509
- $termarr = array( 'slug' => $term['slug'], 'description' => $description, 'parent' => intval($parent) );
510
-
511
- $id = wp_insert_term( $term['term_name'], $term['term_taxonomy'], $termarr );
512
- if ( ! is_wp_error( $id ) ) {
513
- if ( isset($term['term_id']) )
514
- $this->processed_terms[intval($term['term_id'])] = $id['term_id'];
515
- } else {
516
- printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc_html($term['term_taxonomy']), esc_html($term['term_name']) );
517
- if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
518
- echo ': ' . $id->get_error_message();
519
- echo '<br />';
520
- continue;
521
- }
522
-
523
- $this->process_termmeta( $term, $id['term_id'] );
524
- }
525
-
526
- unset( $this->terms );
527
- }
528
-
529
- /**
530
- * Add metadata to imported term.
531
- *
532
- * @since 0.6.2
533
- *
534
- * @param array $term Term data from WXR import.
535
- * @param int $term_id ID of the newly created term.
536
- */
537
- protected function process_termmeta( $term, $term_id ) {
538
- if ( ! isset( $term['termmeta'] ) ) {
539
- $term['termmeta'] = array();
540
- }
541
-
542
- /**
543
- * Filters the metadata attached to an imported term.
544
- *
545
- * @since 0.6.2
546
- *
547
- * @param array $termmeta Array of term meta.
548
- * @param int $term_id ID of the newly created term.
549
- * @param array $term Term data from the WXR import.
550
- */
551
- $term['termmeta'] = apply_filters( 'wp_import_term_meta', $term['termmeta'], $term_id, $term );
552
-
553
- if ( empty( $term['termmeta'] ) ) {
554
- return;
555
- }
556
-
557
- foreach ( $term['termmeta'] as $meta ) {
558
- /**
559
- * Filters the meta key for an imported piece of term meta.
560
- *
561
- * @since 0.6.2
562
- *
563
- * @param string $meta_key Meta key.
564
- * @param int $term_id ID of the newly created term.
565
- * @param array $term Term data from the WXR import.
566
- */
567
- $key = apply_filters( 'import_term_meta_key', $meta['key'], $term_id, $term );
568
- if ( ! $key ) {
569
- continue;
570
- }
571
-
572
- // Export gets meta straight from the DB so could have a serialized string
573
- $value = maybe_unserialize( $meta['value'] );
574
-
575
- add_term_meta( $term_id, $key, $value );
576
-
577
- /**
578
- * Fires after term meta is imported.
579
- *
580
- * @since 0.6.2
581
- *
582
- * @param int $term_id ID of the newly created term.
583
- * @param string $key Meta key.
584
- * @param mixed $value Meta value.
585
- */
586
- do_action( 'import_term_meta', $term_id, $key, $value );
587
- }
588
- }
589
-
590
- /**
591
- * Create new posts based on import information
592
- *
593
- * Posts marked as having a parent which doesn't exist will become top level items.
594
- * Doesn't create a new post if: the post type doesn't exist, the given post ID
595
- * is already noted as imported or a post with the same title and date already exists.
596
- * Note that new/updated terms, comments and meta are imported for the last of the above.
597
- */
598
- function process_posts() {
599
- $this->posts = apply_filters( 'wp_import_posts', $this->posts );
600
-
601
- foreach ( $this->posts as $post ) {
602
- $post = apply_filters( 'wp_import_post_data_raw', $post );
603
-
604
- if ( ! post_type_exists( $post['post_type'] ) ) {
605
- printf( __( 'Failed to import &#8220;%s&#8221;: Invalid post type %s', 'wordpress-importer' ),
606
- esc_html($post['post_title']), esc_html($post['post_type']) );
607
- echo '<br />';
608
- do_action( 'wp_import_post_exists', $post );
609
- continue;
610
- }
611
-
612
- if ( isset( $this->processed_posts[$post['post_id']] ) && ! empty( $post['post_id'] ) )
613
- continue;
614
-
615
- if ( $post['status'] == 'auto-draft' )
616
- continue;
617
-
618
- if ( 'nav_menu_item' == $post['post_type'] ) {
619
- $this->process_menu_item( $post );
620
- continue;
621
- }
622
-
623
- $post_type_object = get_post_type_object( $post['post_type'] );
624
-
625
- $post_exists = post_exists( $post['post_title'], '', $post['post_date'] );
626
-
627
- /**
628
- * Filter ID of the existing post corresponding to post currently importing.
629
- *
630
- * Return 0 to force the post to be imported. Filter the ID to be something else
631
- * to override which existing post is mapped to the imported post.
632
- *
633
- * @see post_exists()
634
- * @since 0.6.2
635
- *
636
- * @param int $post_exists Post ID, or 0 if post did not exist.
637
- * @param array $post The post array to be inserted.
638
- */
639
- $post_exists = apply_filters( 'wp_import_existing_post', $post_exists, $post );
640
-
641
- if ( $post_exists && get_post_type( $post_exists ) == $post['post_type'] ) {
642
- printf( __('%s &#8220;%s&#8221; already exists.', 'wordpress-importer'), $post_type_object->labels->singular_name, esc_html($post['post_title']) );
643
- echo '<br />';
644
- $comment_post_ID = $post_id = $post_exists;
645
- $this->processed_posts[ intval( $post['post_id'] ) ] = intval( $post_exists );
646
- } else {
647
- $post_parent = (int) $post['post_parent'];
648
- if ( $post_parent ) {
649
- // if we already know the parent, map it to the new local ID
650
- if ( isset( $this->processed_posts[$post_parent] ) ) {
651
- $post_parent = $this->processed_posts[$post_parent];
652
- // otherwise record the parent for later
653
- } else {
654
- $this->post_orphans[intval($post['post_id'])] = $post_parent;
655
- $post_parent = 0;
656
- }
657
- }
658
-
659
- // map the post author
660
- $author = sanitize_user( $post['post_author'], true );
661
- if ( isset( $this->author_mapping[$author] ) )
662
- $author = $this->author_mapping[$author];
663
- else
664
- $author = (int) get_current_user_id();
665
-
666
- $postdata = array(
667
- 'import_id' => $post['post_id'], 'post_author' => $author, 'post_date' => $post['post_date'],
668
- 'post_date_gmt' => $post['post_date_gmt'], 'post_content' => $post['post_content'],
669
- 'post_excerpt' => $post['post_excerpt'], 'post_title' => $post['post_title'],
670
- 'post_status' => $post['status'], 'post_name' => $post['post_name'],
671
- 'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'],
672
- 'guid' => $post['guid'], 'post_parent' => $post_parent, 'menu_order' => $post['menu_order'],
673
- 'post_type' => $post['post_type'], 'post_password' => $post['post_password']
674
- );
675
-
676
- $original_post_ID = $post['post_id'];
677
- $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $post );
678
-
679
- $postdata = wp_slash( $postdata );
680
-
681
- if ( 'attachment' == $postdata['post_type'] ) {
682
- $remote_url = ! empty($post['attachment_url']) ? $post['attachment_url'] : $post['guid'];
683
-
684
- // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
685
- // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
686
- $postdata['upload_date'] = $post['post_date'];
687
- if ( isset( $post['postmeta'] ) ) {
688
- foreach( $post['postmeta'] as $meta ) {
689
- if ( $meta['key'] == '_wp_attached_file' ) {
690
- if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta['value'], $matches ) )
691
- $postdata['upload_date'] = $matches[0];
692
- break;
693
- }
694
- }
695
- }
696
-
697
- $comment_post_ID = $post_id = $this->process_attachment( $postdata, $remote_url );
698
- } else {
699
- $comment_post_ID = $post_id = wp_insert_post( $postdata, true );
700
- do_action( 'wp_import_insert_post', $post_id, $original_post_ID, $postdata, $post );
701
- }
702
-
703
- if ( is_wp_error( $post_id ) ) {
704
- printf( __( 'Failed to import %s &#8220;%s&#8221;', 'wordpress-importer' ),
705
- $post_type_object->labels->singular_name, esc_html($post['post_title']) );
706
- if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
707
- echo ': ' . $post_id->get_error_message();
708
- echo '<br />';
709
- continue;
710
- }
711
-
712
- if ( $post['is_sticky'] == 1 )
713
- stick_post( $post_id );
714
- }
715
-
716
- // map pre-import ID to local ID
717
- $this->processed_posts[intval($post['post_id'])] = (int) $post_id;
718
-
719
- if ( ! isset( $post['terms'] ) )
720
- $post['terms'] = array();
721
-
722
- $post['terms'] = apply_filters( 'wp_import_post_terms', $post['terms'], $post_id, $post );
723
-
724
- // add categories, tags and other terms
725
- if ( ! empty( $post['terms'] ) ) {
726
- $terms_to_set = array();
727
- foreach ( $post['terms'] as $term ) {
728
- // back compat with WXR 1.0 map 'tag' to 'post_tag'
729
- $taxonomy = ( 'tag' == $term['domain'] ) ? 'post_tag' : $term['domain'];
730
- $term_exists = term_exists( $term['slug'], $taxonomy );
731
- $term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
732
- if ( ! $term_id ) {
733
- $t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
734
- if ( ! is_wp_error( $t ) ) {
735
- $term_id = $t['term_id'];
736
- do_action( 'wp_import_insert_term', $t, $term, $post_id, $post );
737
- } else {
738
- printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc_html($taxonomy), esc_html($term['name']) );
739
- if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
740
- echo ': ' . $t->get_error_message();
741
- echo '<br />';
742
- do_action( 'wp_import_insert_term_failed', $t, $term, $post_id, $post );
743
- continue;
744
- }
745
- }
746
- $terms_to_set[$taxonomy][] = intval( $term_id );
747
- }
748
-
749
- foreach ( $terms_to_set as $tax => $ids ) {
750
- $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
751
- do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $post );
752
- }
753
- unset( $post['terms'], $terms_to_set );
754
- }
755
-
756
- if ( ! isset( $post['comments'] ) )
757
- $post['comments'] = array();
758
-
759
- $post['comments'] = apply_filters( 'wp_import_post_comments', $post['comments'], $post_id, $post );
760
-
761
- // add/update comments
762
- if ( ! empty( $post['comments'] ) ) {
763
- $num_comments = 0;
764
- $inserted_comments = array();
765
- foreach ( $post['comments'] as $comment ) {
766
- $comment_id = $comment['comment_id'];
767
- $newcomments[$comment_id]['comment_post_ID'] = $comment_post_ID;
768
- $newcomments[$comment_id]['comment_author'] = $comment['comment_author'];
769
- $newcomments[$comment_id]['comment_author_email'] = $comment['comment_author_email'];
770
- $newcomments[$comment_id]['comment_author_IP'] = $comment['comment_author_IP'];
771
- $newcomments[$comment_id]['comment_author_url'] = $comment['comment_author_url'];
772
- $newcomments[$comment_id]['comment_date'] = $comment['comment_date'];
773
- $newcomments[$comment_id]['comment_date_gmt'] = $comment['comment_date_gmt'];
774
- $newcomments[$comment_id]['comment_content'] = $comment['comment_content'];
775
- $newcomments[$comment_id]['comment_approved'] = $comment['comment_approved'];
776
- $newcomments[$comment_id]['comment_type'] = $comment['comment_type'];
777
- $newcomments[$comment_id]['comment_parent'] = $comment['comment_parent'];
778
- $newcomments[$comment_id]['commentmeta'] = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
779
- if ( isset( $this->processed_authors[$comment['comment_user_id']] ) )
780
- $newcomments[$comment_id]['user_id'] = $this->processed_authors[$comment['comment_user_id']];
781
- }
782
- ksort( $newcomments );
783
-
784
- foreach ( $newcomments as $key => $comment ) {
785
- // if this is a new post we can skip the comment_exists() check
786
- if ( ! $post_exists || ! comment_exists( $comment['comment_author'], $comment['comment_date'] ) ) {
787
- if ( isset( $inserted_comments[$comment['comment_parent']] ) )
788
- $comment['comment_parent'] = $inserted_comments[$comment['comment_parent']];
789
- $comment = wp_filter_comment( $comment );
790
- $inserted_comments[$key] = wp_insert_comment( $comment );
791
- do_action( 'wp_import_insert_comment', $inserted_comments[$key], $comment, $comment_post_ID, $post );
792
-
793
- foreach( $comment['commentmeta'] as $meta ) {
794
- $value = maybe_unserialize( $meta['value'] );
795
- add_comment_meta( $inserted_comments[$key], $meta['key'], $value );
796
- }
797
-
798
- $num_comments++;
799
- }
800
- }
801
- unset( $newcomments, $inserted_comments, $post['comments'] );
802
- }
803
-
804
- if ( ! isset( $post['postmeta'] ) )
805
- $post['postmeta'] = array();
806
-
807
- $post['postmeta'] = apply_filters( 'wp_import_post_meta', $post['postmeta'], $post_id, $post );
808
-
809
- // add/update post meta
810
- if ( ! empty( $post['postmeta'] ) ) {
811
- foreach ( $post['postmeta'] as $meta ) {
812
- $key = apply_filters( 'import_post_meta_key', $meta['key'], $post_id, $post );
813
- $value = false;
814
-
815
- if ( '_edit_last' == $key ) {
816
- if ( isset( $this->processed_authors[intval($meta['value'])] ) )
817
- $value = $this->processed_authors[intval($meta['value'])];
818
- else
819
- $key = false;
820
- }
821
-
822
- if ( $key ) {
823
- // export gets meta straight from the DB so could have a serialized string
824
- if ( ! $value )
825
- $value = maybe_unserialize( $meta['value'] );
826
-
827
- add_post_meta( $post_id, $key, $value );
828
- do_action( 'import_post_meta', $post_id, $key, $value );
829
-
830
- // if the post has a featured image, take note of this in case of remap
831
- if ( '_thumbnail_id' == $key )
832
- $this->featured_images[$post_id] = (int) $value;
833
- }
834
- }
835
- }
836
- }
837
-
838
- unset( $this->posts );
839
- }
840
-
841
- /**
842
- * Attempt to create a new menu item from import data
843
- *
844
- * Fails for draft, orphaned menu items and those without an associated nav_menu
845
- * or an invalid nav_menu term. If the post type or term object which the menu item
846
- * represents doesn't exist then the menu item will not be imported (waits until the
847
- * end of the import to retry again before discarding).
848
- *
849
- * @param array $item Menu item details from WXR file
850
- */
851
- function process_menu_item( $item ) {
852
- // skip draft, orphaned menu items
853
- if ( 'draft' == $item['status'] )
854
- return;
855
-
856
- $menu_slug = false;
857
- if ( isset($item['terms']) ) {
858
- // loop through terms, assume first nav_menu term is correct menu
859
- foreach ( $item['terms'] as $term ) {
860
- if ( 'nav_menu' == $term['domain'] ) {
861
- $menu_slug = $term['slug'];
862
- break;
863
- }
864
- }
865
- }
866
-
867
- // no nav_menu term associated with this menu item
868
- if ( ! $menu_slug ) {
869
- _e( 'Menu item skipped due to missing menu slug', 'wordpress-importer' );
870
- echo '<br />';
871
- return;
872
- }
873
-
874
- $menu_id = term_exists( $menu_slug, 'nav_menu' );
875
- if ( ! $menu_id ) {
876
- printf( __( 'Menu item skipped due to invalid menu slug: %s', 'wordpress-importer' ), esc_html( $menu_slug ) );
877
- echo '<br />';
878
- return;
879
- } else {
880
- $menu_id = is_array( $menu_id ) ? $menu_id['term_id'] : $menu_id;
881
- }
882
-
883
- if ( version_compare( PHP_VERSION, '7.0' ) >= 0 ) {
884
- foreach ( $item['postmeta'] as $meta ) {
885
- ${$meta['key']} = $meta['value'];
886
- }
887
- } else {
888
- foreach ( $item['postmeta'] as $meta ) {
889
- $$meta['key'] = $meta['value'];
890
- }
891
- }
892
-
893
- if ( 'taxonomy' == $_menu_item_type && isset( $this->processed_terms[intval($_menu_item_object_id)] ) ) {
894
- $_menu_item_object_id = $this->processed_terms[intval($_menu_item_object_id)];
895
- } else if ( 'post_type' == $_menu_item_type && isset( $this->processed_posts[intval($_menu_item_object_id)] ) ) {
896
- $_menu_item_object_id = $this->processed_posts[intval($_menu_item_object_id)];
897
- } else if ( 'custom' != $_menu_item_type ) {
898
- // associated object is missing or not imported yet, we'll retry later
899
- $this->missing_menu_items[] = $item;
900
- return;
901
- }
902
-
903
- if ( isset( $this->processed_menu_items[intval($_menu_item_menu_item_parent)] ) ) {
904
- $_menu_item_menu_item_parent = $this->processed_menu_items[intval($_menu_item_menu_item_parent)];
905
- } else if ( $_menu_item_menu_item_parent ) {
906
- $this->menu_item_orphans[intval($item['post_id'])] = (int) $_menu_item_menu_item_parent;
907
- $_menu_item_menu_item_parent = 0;
908
- }
909
-
910
- // wp_update_nav_menu_item expects CSS classes as a space separated string
911
- $_menu_item_classes = maybe_unserialize( $_menu_item_classes );
912
- if ( is_array( $_menu_item_classes ) )
913
- $_menu_item_classes = implode( ' ', $_menu_item_classes );
914
-
915
- $args = array(
916
- 'menu-item-object-id' => $_menu_item_object_id,
917
- 'menu-item-object' => $_menu_item_object,
918
- 'menu-item-parent-id' => $_menu_item_menu_item_parent,
919
- 'menu-item-position' => intval( $item['menu_order'] ),
920
- 'menu-item-type' => $_menu_item_type,
921
- 'menu-item-title' => $item['post_title'],
922
- 'menu-item-url' => $_menu_item_url,
923
- 'menu-item-description' => $item['post_content'],
924
- 'menu-item-attr-title' => $item['post_excerpt'],
925
- 'menu-item-target' => $_menu_item_target,
926
- 'menu-item-classes' => $_menu_item_classes,
927
- 'menu-item-xfn' => $_menu_item_xfn,
928
- 'menu-item-status' => $item['status']
929
- );
930
-
931
- $id = wp_update_nav_menu_item( $menu_id, 0, $args );
932
- if ( $id && ! is_wp_error( $id ) )
933
- $this->processed_menu_items[intval($item['post_id'])] = (int) $id;
934
- }
935
-
936
- /**
937
- * If fetching attachments is enabled then attempt to create a new attachment
938
- *
939
- * @param array $post Attachment post details from WXR
940
- * @param string $url URL to fetch attachment from
941
- * @return int|WP_Error Post ID on success, WP_Error otherwise
942
- */
943
- function process_attachment( $post, $url ) {
944
- if ( ! $this->fetch_attachments )
945
- return new WP_Error( 'attachment_processing_error',
946
- __( 'Fetching attachments is not enabled', 'wordpress-importer' ) );
947
-
948
- // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
949
- if ( preg_match( '|^/[\w\W]+$|', $url ) )
950
- $url = rtrim( $this->base_url, '/' ) . $url;
951
-
952
- $upload = $this->fetch_remote_file( $url, $post );
953
- if ( is_wp_error( $upload ) )
954
- return $upload;
955
-
956
- if ( $info = wp_check_filetype( $upload['file'] ) )
957
- $post['post_mime_type'] = $info['type'];
958
- else
959
- return new WP_Error( 'attachment_processing_error', __('Invalid file type', 'wordpress-importer') );
960
-
961
- $post['guid'] = $upload['url'];
962
-
963
- // as per wp-admin/includes/upload.php
964
- $post_id = wp_insert_attachment( $post, $upload['file'] );
965
- wp_update_attachment_metadata( $post_id, wp_generate_attachment_metadata( $post_id, $upload['file'] ) );
966
-
967
- // remap resized image URLs, works by stripping the extension and remapping the URL stub.
968
- if ( preg_match( '!^image/!', $info['type'] ) ) {
969
- $parts = pathinfo( $url );
970
- $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
971
-
972
- $parts_new = pathinfo( $upload['url'] );
973
- $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
974
-
975
- $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
976
- }
977
-
978
- return $post_id;
979
- }
980
-
981
- /**
982
- * Attempt to download a remote file attachment
983
- *
984
- * @param string $url URL of item to fetch
985
- * @param array $post Attachment details
986
- * @return array|WP_Error Local file location details on success, WP_Error otherwise
987
- */
988
- function fetch_remote_file( $url, $post ) {
989
- // extract the file name and extension from the url
990
- $file_name = basename( $url );
991
-
992
- // get placeholder file in the upload dir with a unique, sanitized filename
993
- $upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
994
- if ( $upload['error'] )
995
- return new WP_Error( 'upload_dir_error', $upload['error'] );
996
-
997
- // fetch the remote url and write it to the placeholder file
998
- $headers = wp_get_http( $url, $upload['file'] );
999
-
1000
- // request failed
1001
- if ( ! $headers ) {
1002
- @unlink( $upload['file'] );
1003
- return new WP_Error( 'import_file_error', __('Remote server did not respond', 'wordpress-importer') );
1004
- }
1005
-
1006
- // make sure the fetch was successful
1007
- if ( $headers['response'] != '200' ) {
1008
- @unlink( $upload['file'] );
1009
- return new WP_Error( 'import_file_error', sprintf( __('Remote server returned error response %1$d %2$s', 'wordpress-importer'), esc_html($headers['response']), get_status_header_desc($headers['response']) ) );
1010
- }
1011
-
1012
- $filesize = filesize( $upload['file'] );
1013
-
1014
- if ( isset( $headers['content-length'] ) && $filesize != $headers['content-length'] ) {
1015
- @unlink( $upload['file'] );
1016
- return new WP_Error( 'import_file_error', __('Remote file is incorrect size', 'wordpress-importer') );
1017
- }
1018
-
1019
- if ( 0 == $filesize ) {
1020
- @unlink( $upload['file'] );
1021
- return new WP_Error( 'import_file_error', __('Zero size file downloaded', 'wordpress-importer') );
1022
- }
1023
-
1024
- $max_size = (int) $this->max_attachment_size();
1025
- if ( ! empty( $max_size ) && $filesize > $max_size ) {
1026
- @unlink( $upload['file'] );
1027
- return new WP_Error( 'import_file_error', sprintf(__('Remote file is too large, limit is %s', 'wordpress-importer'), size_format($max_size) ) );
1028
- }
1029
-
1030
- // keep track of the old and new urls so we can substitute them later
1031
- $this->url_remap[$url] = $upload['url'];
1032
- $this->url_remap[$post['guid']] = $upload['url']; // r13735, really needed?
1033
- // keep track of the destination if the remote url is redirected somewhere else
1034
- if ( isset($headers['x-final-location']) && $headers['x-final-location'] != $url )
1035
- $this->url_remap[$headers['x-final-location']] = $upload['url'];
1036
-
1037
- return $upload;
1038
- }
1039
-
1040
- /**
1041
- * Attempt to associate posts and menu items with previously missing parents
1042
- *
1043
- * An imported post's parent may not have been imported when it was first created
1044
- * so try again. Similarly for child menu items and menu items which were missing
1045
- * the object (e.g. post) they represent in the menu
1046
- */
1047
- function backfill_parents() {
1048
- global $wpdb;
1049
-
1050
- // find parents for post orphans
1051
- foreach ( $this->post_orphans as $child_id => $parent_id ) {
1052
- $local_child_id = $local_parent_id = false;
1053
- if ( isset( $this->processed_posts[$child_id] ) )
1054
- $local_child_id = $this->processed_posts[$child_id];
1055
- if ( isset( $this->processed_posts[$parent_id] ) )
1056
- $local_parent_id = $this->processed_posts[$parent_id];
1057
-
1058
- if ( $local_child_id && $local_parent_id )
1059
- $wpdb->update( $wpdb->posts, array( 'post_parent' => $local_parent_id ), array( 'ID' => $local_child_id ), '%d', '%d' );
1060
- }
1061
-
1062
- // all other posts/terms are imported, retry menu items with missing associated object
1063
- $missing_menu_items = $this->missing_menu_items;
1064
- foreach ( $missing_menu_items as $item )
1065
- $this->process_menu_item( $item );
1066
-
1067
- // find parents for menu item orphans
1068
- foreach ( $this->menu_item_orphans as $child_id => $parent_id ) {
1069
- $local_child_id = $local_parent_id = 0;
1070
- if ( isset( $this->processed_menu_items[$child_id] ) )
1071
- $local_child_id = $this->processed_menu_items[$child_id];
1072
- if ( isset( $this->processed_menu_items[$parent_id] ) )
1073
- $local_parent_id = $this->processed_menu_items[$parent_id];
1074
-
1075
- if ( $local_child_id && $local_parent_id )
1076
- update_post_meta( $local_child_id, '_menu_item_menu_item_parent', (int) $local_parent_id );
1077
- }
1078
- }
1079
-
1080
- /**
1081
- * Use stored mapping information to update old attachment URLs
1082
- */
1083
- function backfill_attachment_urls() {
1084
- global $wpdb;
1085
- // make sure we do the longest urls first, in case one is a substring of another
1086
- uksort( $this->url_remap, array(&$this, 'cmpr_strlen') );
1087
-
1088
- foreach ( $this->url_remap as $from_url => $to_url ) {
1089
- // remap urls in post_content
1090
- $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url) );
1091
- // remap enclosure urls
1092
- $result = $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url) );
1093
- }
1094
- }
1095
-
1096
- /**
1097
- * Update _thumbnail_id meta to new, imported attachment IDs
1098
- */
1099
- function remap_featured_images() {
1100
- // cycle through posts that have a featured image
1101
- foreach ( $this->featured_images as $post_id => $value ) {
1102
- if ( isset( $this->processed_posts[$value] ) ) {
1103
- $new_id = $this->processed_posts[$value];
1104
- // only update if there's a difference
1105
- if ( $new_id != $value )
1106
- update_post_meta( $post_id, '_thumbnail_id', $new_id );
1107
- }
1108
- }
1109
- }
1110
-
1111
- /**
1112
- * Parse a WXR file
1113
- *
1114
- * @param string $file Path to WXR file for parsing
1115
- * @return array Information gathered from the WXR file
1116
- */
1117
- function parse( $file ) {
1118
- $parser = new WXR_Parser();
1119
- return $parser->parse( $file );
1120
- }
1121
-
1122
- // Display import page title
1123
- function header() {
1124
- echo '<div class="wrap">';
1125
- screen_icon();
1126
- echo '<h2>' . __( 'Import WordPress', 'wordpress-importer' ) . '</h2>';
1127
-
1128
- $updates = get_plugin_updates();
1129
- $basename = plugin_basename(__FILE__);
1130
- if ( isset( $updates[$basename] ) ) {
1131
- $update = $updates[$basename];
1132
- echo '<div class="error"><p><strong>';
1133
- printf( __( 'A new version of this importer is available. Please update to version %s to ensure compatibility with newer export files.', 'wordpress-importer' ), $update->update->new_version );
1134
- echo '</strong></p></div>';
1135
- }
1136
- }
1137
-
1138
- // Close div.wrap
1139
- function footer() {
1140
- echo '</div>';
1141
- }
1142
-
1143
- /**
1144
- * Display introductory text and file upload form
1145
- */
1146
- function greet() {
1147
- echo '<div class="narrow">';
1148
- echo '<p>'.__( 'Howdy! Upload your WordPress eXtended RSS (WXR) file and we&#8217;ll import the posts, pages, comments, custom fields, categories, and tags into this site.', 'wordpress-importer' ).'</p>';
1149
- echo '<p>'.__( 'Choose a WXR (.xml) file to upload, then click Upload file and import.', 'wordpress-importer' ).'</p>';
1150
- wp_import_upload_form( 'admin.php?import=wordpress&amp;step=1' );
1151
- echo '</div>';
1152
- }
1153
-
1154
- /**
1155
- * Decide if the given meta key maps to information we will want to import
1156
- *
1157
- * @param string $key The meta key to check
1158
- * @return string|bool The key if we do want to import, false if not
1159
- */
1160
- function is_valid_meta_key( $key ) {
1161
- // skip attachment metadata since we'll regenerate it from scratch
1162
- // skip _edit_lock as not relevant for import
1163
- if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) )
1164
- return false;
1165
- return $key;
1166
- }
1167
-
1168
- /**
1169
- * Decide whether or not the importer is allowed to create users.
1170
- * Default is true, can be filtered via import_allow_create_users
1171
- *
1172
- * @return bool True if creating users is allowed
1173
- */
1174
- function allow_create_users() {
1175
- return apply_filters( 'import_allow_create_users', true );
1176
- }
1177
-
1178
- /**
1179
- * Decide whether or not the importer should attempt to download attachment files.
1180
- * Default is true, can be filtered via import_allow_fetch_attachments. The choice
1181
- * made at the import options screen must also be true, false here hides that checkbox.
1182
- *
1183
- * @return bool True if downloading attachments is allowed
1184
- */
1185
- function allow_fetch_attachments() {
1186
- return apply_filters( 'import_allow_fetch_attachments', true );
1187
- }
1188
-
1189
- /**
1190
- * Decide what the maximum file size for downloaded attachments is.
1191
- * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
1192
- *
1193
- * @return int Maximum attachment file size to import
1194
- */
1195
- function max_attachment_size() {
1196
- return apply_filters( 'import_attachment_size_limit', 0 );
1197
- }
1198
-
1199
- /**
1200
- * Added to http_request_timeout filter to force timeout at 60 seconds during import
1201
- * @return int 60
1202
- */
1203
- function bump_request_timeout( $val ) {
1204
- return 60;
1205
- }
1206
-
1207
- // return the difference in length between two strings
1208
- function cmpr_strlen( $a, $b ) {
1209
- return strlen($b) - strlen($a);
1210
- }
1211
- }
1212
-
1213
- } // class_exists( 'WP_Importer' )
1214
-
1215
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
- === Plugin Name ===
2
  Contributors: famethemes, shrimp2t
3
  Donate link: https://www.famethemes.com/
4
  Tags: import, demo data, oneclick, famethemes
5
  Requires at least: 4.5
6
  Tested up to: 4.6
7
- Stable tag: 4.6
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -12,23 +12,73 @@ FameThemes Demo importer
12
 
13
  == Description ==
14
 
15
- Import demo content for FameThemes’s themes with just one click.
16
 
17
- Working with themes:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- - [Screenr](https://wordpress.org/themes/screenr/)
20
- - [Boston](https://wordpress.org/themes/boston/).
21
- - [OnePress](https://wordpress.org/themes/onepress/)
22
 
23
  == Installation ==
24
 
25
  1. Upload `famethemes-demo-importer` folder to the `/wp-content/plugins/` directory
26
  2. Activate the plugin through the 'Plugins' menu in WordPress
27
- 3. Appereance -> (Theme Name) -> Select tab One Click Demo Import
28
 
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  == Changelog ==
 
 
 
 
 
 
32
  = 1.0.2
33
  * Add recommend plugins notices.
34
 
1
+ === FameTheme Demo Importer ===
2
  Contributors: famethemes, shrimp2t
3
  Donate link: https://www.famethemes.com/
4
  Tags: import, demo data, oneclick, famethemes
5
  Requires at least: 4.5
6
  Tested up to: 4.6
7
+ Stable tag: 1.0.7
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
12
 
13
  == Description ==
14
 
15
+ Import your demo content, widgets and theme settings with one click for [FameThemes](https://www.famethemes.com/) official themes.
16
 
17
+ Get free support at [https://www.famethemes.com/]((https://www.famethemes.com/))
18
+
19
+ https://www.youtube.com/watch?v=w0OKnqnHYo4
20
+
21
+ ##Add Support for your themes.
22
+
23
+ ### Change Default Demo GitHub Repository.
24
+
25
+ `apply_filters( 'demo_contents_github_repo', self::$git_repo );`
26
+
27
+ ### Add theme to listing preview
28
+
29
+ `apply_filters( 'demo_contents_allowed_authors', array('famethemes' => 'FameThemes','daisy themes' => 'Daisy Themes'};`
30
+
31
+ ###Support demo for a theme.
32
+ 1. Create new theme demo dir in GitHub repo `username/repo-name/theme-name`.
33
+
34
+ ###Support multiple demos for a theme.
35
+ 1. Create new theme demo dir in GitHub repo `username/repo-name/theme-name`.
36
+ 2. Create new json file and name it `demos.json`, add list demos here.
37
+ 3. Crate new demo dir and name it `demos`.
38
+ 4. Add your new demo in new dir `child-demo`, so we have full path like this: `username/repo-name/theme-name/demos/child-demo` and put file `dummy-data.xml` and `config.json`
39
+
40
+ ###Export Demo XML
41
+ In Admin screen go to Tools -> Export
42
+
43
+ ###Export config.json
44
+
45
+ In Admin if user has cap `export`, add ?demo_contents_export in current url.
46
+ Example: https://example.com/wp-admin/?demo_contents_export
47
 
 
 
 
48
 
49
  == Installation ==
50
 
51
  1. Upload `famethemes-demo-importer` folder to the `/wp-content/plugins/` directory
52
  2. Activate the plugin through the 'Plugins' menu in WordPress
53
+ 3. Appereance -> (Theme Name) -> Select tab One Click Demo Import or Tools -> Demo Contents to select demo to import.
54
 
55
 
56
+ == Frequently Asked Questions ==
57
+
58
+ = What is the plugin license? =
59
+
60
+ * This plugin is released under a GPL license.
61
+
62
+ = What themes this plugin supports? =
63
+
64
+ * The plugin currently only supports FameThemes's themes.
65
+
66
+ = Where can I report bugs or contribute to the project? =
67
+
68
+ Bugs can be reported either in our support forum or preferably on the [GitHub repository](https://github.com/FameThemes/famethemes-demo-importer/issues).
69
+
70
+ = FameThemes Demo Importer is awesome! Can I contribute? =
71
+
72
+ Yes you can! Join in on our [GitHub repository](https://github.com/FameThemes/famethemes-demo-importer/) :)
73
+
74
 
75
  == Changelog ==
76
+ = 1.0.7
77
+ * Improve import username.
78
+
79
+ = 1.0.6
80
+ * Improve core and UX.
81
+
82
  = 1.0.2
83
  * Add recommend plugins notices.
84
 
style.css ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .demo-contents-import-box {
2
+ background: #fff none repeat scroll 0 0;
3
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
4
+ font-size: 14px;
5
+ line-height: 2.1em;
6
+ margin-bottom: 25px;
7
+ overflow: auto;
8
+ padding: 15px;
9
+ position: relative;
10
+ max-width: 1050px;
11
+ }
12
+
13
+ .demo-contents-import-box.demo-contents-welcome {
14
+ padding: 0px 20px 10px 25px;
15
+ }
16
+
17
+ .demo-contents-import-box h3 {
18
+ font-size: 22px;
19
+ font-weight: 500;
20
+ margin: 0px 0px 15px;
21
+ }
22
+
23
+ .demo-contents-import-box li {
24
+ margin: 0px;
25
+ padding-left: 15px;
26
+ list-style: inside;
27
+ }
28
+
29
+ .demo-contents--current-theme {
30
+ position: relative;
31
+ }
32
+ .demo-contents--current-theme .more-details {
33
+ opacity: 1 !important;
34
+ z-index: 3;
35
+ }
36
+
37
+ .demo-contents-hide {
38
+ display: none !important;
39
+ }
40
+
41
+ .demo-contents .more-details {
42
+ text-decoration: none !important;
43
+ }
44
+
45
+ .demo-contents .theme-overlay .theme-wrap {
46
+ box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1);
47
+ border: 1px solid #ddd;
48
+ }
49
+
50
+ .demo-contents .theme-overlay .theme-wrap {
51
+ position: relative;
52
+ top: auto;
53
+ bottom: auto;
54
+ left: auto;
55
+ right: auto;
56
+ padding-top: 50%;
57
+ margin-bottom: 40px;
58
+ }
59
+
60
+ .demo-contents .theme-overlay .theme-about {
61
+ top: 25px;
62
+ }
63
+
64
+ .demo-contents .theme-overlay .theme-overlay .theme-wrap {
65
+ z-index: 1;
66
+ }
67
+
68
+ .demo-contents .theme .theme-actions, .demo-import-tab-content .theme .theme-actions {
69
+ opacity: 1;
70
+ }
71
+
72
+ .demo-contents-import-progress {
73
+ background: #ffffff;
74
+ padding: 15px 15px 30px;
75
+ display: block;
76
+ }
77
+
78
+ .demo-contents-child-item {
79
+ margin: 5px 0px;
80
+ }
81
+
82
+ .demo-contents--activate-notice {
83
+ padding: 15px;
84
+ margin: 30px 0px;
85
+ }
86
+ .demo-contents--activate-notice.resources-not-found {
87
+ margin-top: 0px;
88
+ }
89
+
90
+ @-moz-keyframes spin {
91
+ 100% {
92
+ -moz-transform: rotate(360deg);
93
+ }
94
+ }
95
+ @-webkit-keyframes spin {
96
+ 100% {
97
+ -webkit-transform: rotate(360deg);
98
+ }
99
+ }
100
+ @keyframes spin {
101
+ 100% {
102
+ -webkit-transform: rotate(360deg);
103
+ transform: rotate(360deg);
104
+ }
105
+ }
106
+ .demo-contents--theme-thumbnail {
107
+ padding: 10px;
108
+ background: #ddd;
109
+ }
110
+ .demo-contents--theme-thumbnail img {
111
+ max-width: 100%;
112
+ height: auto;
113
+ }
114
+
115
+ .demo-contents--step {
116
+ position: relative;
117
+ cursor: pointer;
118
+ border-bottom: 1px solid #ddd;
119
+ padding-bottom: 10px;
120
+ padding-top: 10px;
121
+ }
122
+ .demo-contents--step:last-child {
123
+ border-bottom: 0px none;
124
+ }
125
+ .demo-contents--step .demo-contents--step-heading {
126
+ font-size: 12px;
127
+ font-weight: bold;
128
+ line-height: 18px;
129
+ }
130
+ .demo-contents--step .demo-contents--child-steps {
131
+ display: none;
132
+ }
133
+ .demo-contents--step .demo-contents--child-steps.demo-contents--show {
134
+ display: block;
135
+ }
136
+ .demo-contents--step .demo-contents--status {
137
+ background: none;
138
+ border: none !important;
139
+ -webkit-box-shadow: none;
140
+ box-shadow: none;
141
+ padding: 0px;
142
+ position: absolute;
143
+ top: 7px;
144
+ right: 0px;
145
+ line-height: 24px;
146
+ width: 24px;
147
+ height: 24px;
148
+ display: block;
149
+ }
150
+ .demo-contents--step .demo-contents--status:before {
151
+ font-family: "dashicons";
152
+ font: 400 24px/1 dashicons;
153
+ -webkit-font-smoothing: antialiased;
154
+ -moz-osx-font-smoothing: grayscale;
155
+ width: 24px;
156
+ height: 24px;
157
+ display: block;
158
+ font-weight: normal;
159
+ }
160
+ .demo-contents--step.demo-contents--running .demo-contents--status {
161
+ -webkit-animation: spin 2s linear infinite;
162
+ -moz-animation: spin 2s linear infinite;
163
+ animation: spin 2s linear infinite;
164
+ color: #f56e28;
165
+ }
166
+ .demo-contents--step.demo-contents--running .demo-contents--status:before {
167
+ content: "\f463";
168
+ }
169
+ .demo-contents--step.demo-contents--waiting .demo-contents--status:before {
170
+ content: "\f469";
171
+ }
172
+ .demo-contents--step.demo-contents--completed .demo-contents--status {
173
+ color: #65ae1f;
174
+ }
175
+ .demo-contents--step.demo-contents--completed .demo-contents--status::before {
176
+ content: "\f147";
177
+ }
178
+
179
+ body.demo-contents-body-viewing {
180
+ overflow: hidden !important;
181
+ }
182
+
183
+ #demo-contents--preview {
184
+ z-index: 99999;
185
+ background: #eee;
186
+ position: fixed;
187
+ top: 0px;
188
+ left: 0px;
189
+ bottom: 0px;
190
+ display: block;
191
+ width: 100%;
192
+ }
193
+ #demo-contents--preview .demo-contents-close {
194
+ display: block;
195
+ float: left;
196
+ width: 45px;
197
+ height: 45px;
198
+ padding-right: 2px;
199
+ background: #eee;
200
+ border-right: 1px solid #ddd;
201
+ color: #444;
202
+ cursor: pointer;
203
+ text-decoration: none;
204
+ position: absolute;
205
+ top: 0px;
206
+ left: 0px;
207
+ -webkit-transition: color .1s ease-in-out,background .1s ease-in-out;
208
+ transition: color .1s ease-in-out,background .1s ease-in-out;
209
+ z-index: 99999;
210
+ }
211
+ #demo-contents--preview .demo-contents-close:before {
212
+ font: 400 22px/1 dashicons;
213
+ content: "\f335";
214
+ position: relative;
215
+ top: 12px;
216
+ left: 13px;
217
+ }
218
+ #demo-contents--preview .demo-contents-collapse-sidebar {
219
+ z-index: 99999;
220
+ bottom: 5px;
221
+ left: 0;
222
+ padding: 9px 0 9px 10px;
223
+ height: 45px;
224
+ color: #656a6f;
225
+ outline: 0;
226
+ line-height: 1;
227
+ background-color: transparent !important;
228
+ border: none !important;
229
+ -webkit-box-shadow: none !important;
230
+ box-shadow: none !important;
231
+ -webkit-border-radius: 0 !important;
232
+ border-radius: 0 !important;
233
+ position: fixed;
234
+ }
235
+ #demo-contents--preview .demo-contents-collapse-sidebar .collapse-sidebar-arrow:before {
236
+ display: block;
237
+ float: left;
238
+ content: "\f148";
239
+ font: 400 20px/1 dashicons;
240
+ speak: none;
241
+ padding: 0;
242
+ -webkit-font-smoothing: antialiased;
243
+ -moz-osx-font-smoothing: grayscale;
244
+ border: 1px solid transparent;
245
+ -webkit-border-radius: 50%;
246
+ -moz-border-radius: 50%;
247
+ border-radius: 50%;
248
+ }
249
+ #demo-contents--preview .demo-contents-collapse-sidebar .collapse-sidebar-label {
250
+ display: inline-block;
251
+ vertical-align: middle;
252
+ line-height: 20px;
253
+ margin-left: 5px;
254
+ -webkit-transition: all .3s;
255
+ /* For Safari 3.1 to 6.0 */
256
+ transition: all .3s;
257
+ }
258
+ #demo-contents--preview .demo-contents-collapse-sidebar:hover {
259
+ color: #0073aa;
260
+ }
261
+ #demo-contents--preview .demo-contents-collapse-sidebar:hover .collapse-sidebar-arrow:before {
262
+ border-color: #0073aa;
263
+ }
264
+ #demo-contents--preview #demo-contents-sidebar-topbar {
265
+ display: block;
266
+ text-align: right;
267
+ border-bottom: 1px solid #ddd;
268
+ padding: 10px 10px 10px 55px;
269
+ background: #eee;
270
+ height: 25px;
271
+ }
272
+ #demo-contents--preview .ft-theme-name {
273
+ font-size: 20px;
274
+ font-weight: bold;
275
+ line-height: 24px;
276
+ }
277
+ #demo-contents--preview #demo-contents-sidebar {
278
+ width: 300px;
279
+ top: 0px;
280
+ left: 0px;
281
+ bottom: 0px;
282
+ display: block;
283
+ border-right: 1px solid #ddd;
284
+ position: fixed;
285
+ background: #fff;
286
+ -webkit-transition: all .3s;
287
+ /* For Safari 3.1 to 6.0 */
288
+ transition: all .3s;
289
+ overflow: hidden;
290
+ }
291
+ #demo-contents--preview.ft-preview-collapse #demo-contents-sidebar {
292
+ left: -301px;
293
+ }
294
+ #demo-contents--preview.ft-preview-collapse #demo-contents-viewing {
295
+ margin-left: 0px;
296
+ }
297
+ #demo-contents--preview.ft-preview-collapse .collapse-sidebar-label {
298
+ opacity: 0 !important;
299
+ }
300
+ #demo-contents--preview #demo-contents-sidebar-heading {
301
+ background: #fff;
302
+ color: #555d66;
303
+ margin: 0;
304
+ padding: 12px 15px 15px;
305
+ position: relative;
306
+ font-size: 20px;
307
+ font-weight: 200;
308
+ line-height: 26px;
309
+ display: block;
310
+ overflow: hidden;
311
+ white-space: nowrap;
312
+ border-bottom: 1px solid #ddd;
313
+ }
314
+ #demo-contents--preview #demo-contents-sidebar-heading span {
315
+ display: block;
316
+ font-size: 12px;
317
+ }
318
+ #demo-contents--preview #demo-contents-viewing {
319
+ margin-left: 301px;
320
+ position: relative;
321
+ height: 100%;
322
+ display: block;
323
+ -webkit-transition: all .3s;
324
+ /* For Safari 3.1 to 6.0 */
325
+ transition: all .3s;
326
+ overflow: hidden;
327
+ }
328
+ #demo-contents--preview #demo-contents-viewing iframe {
329
+ width: 100%;
330
+ height: 100%;
331
+ display: block;
332
+ }
333
+ #demo-contents--preview #demo-contents-sidebar-content {
334
+ position: absolute;
335
+ top: 46px;
336
+ bottom: 49px;
337
+ display: block;
338
+ left: 0px;
339
+ width: 100%;
340
+ overflow: auto;
341
+ }
342
+ #demo-contents--preview #demo-contents-sidebar-footer {
343
+ position: absolute;
344
+ bottom: 0px;
345
+ right: 0px;
346
+ display: block;
347
+ left: 0px;
348
+ padding: 20px 20px;
349
+ text-align: right;
350
+ background: #eee;
351
+ border-top: 1px solid #ddd;
352
+ }
353
+ #demo-contents--preview .demo-contents---upload {
354
+ text-align: center;
355
+ padding: 15px 20px;
356
+ background: #eee;
357
+ }
358
+ #demo-contents--preview .demo-contents---upload button {
359
+ display: block;
360
+ width: 100%;
361
+ }