1+ @if (auth ()-> user ()-> role == ' admin' )
2+ @if (env (' ENABLE_THEME_UPDATER' ) == ' true' )
3+ <style >
4+ details {
5+ width : 65% ;
6+ margin-left : 15px ;
7+
8+ {
9+ {
10+ -- max-width : calc (100% - 20rem );
11+ --
12+ }
13+ }
14+
15+ position: relative;
16+ border: 1px solid #78909C ;
17+ border-radius : 6px;
18+ background-color : #ECEFF1;
19+ color: #263238 ;
20+ transition: background-color .15s ;
21+
22+ > :last-child {
23+ margin-bottom : 1rem ;
24+ }
25+
26+ &::before {
27+ width : 100% ;
28+ height : 100% ;
29+ content : ' ' ;
30+ position : absolute ;
31+ top : 0 ;
32+ left : 0 ;
33+ border-radius : inherit ;
34+ opacity : .15 ;
35+ box-shadow : 0 .25em .5em #263238 ;
36+ pointer-events : none ;
37+ transition : opacity .2s ;
38+ z-index : -1 ;
39+ }
40+
41+ &[open ] {
42+ background-color : #FFF ;
43+
44+ &::before {
45+ opacity: .6 ;
46+ }
47+ }
48+ }
49+
50+ summary {
51+ padding : 0.375rem 0.75rem ;
52+ width : 100% ;
53+ display : block ;
54+ position : relative ;
55+ font-size : 1.33em ;
56+ font-weight : bold ;
57+ cursor : pointer ;
58+
59+ &::before,
60+ &::after {
61+ width: .75em ;
62+ height : 2px ;
63+ position : absolute ;
64+ top : 50% ;
65+ right : 0 ;
66+ content : ' ' ;
67+ background-color : currentColor ;
68+ text-align : right ;
69+ transform : translateY (-50% );
70+ transition : transform .2s ease-in-out ;
71+ }
72+
73+ &::after {
74+ transform : translateY (-50% ) rotate (90deg );
75+
76+ [open ] & {
77+ transform : translateY (-50% ) rotate (180deg );
78+ }
79+ }
80+
81+ &::-webkit-details-marker {
82+ display : none ;
83+ }
84+ }
85+
86+ table ,
87+ th ,
88+ td {
89+ border : 1px solid black ;
90+ }
91+
92+ .updatespin {
93+ animation : upspin 1s linear infinite ;
94+ display : inline-block ;
95+ }
96+
97+ @keyframes upspin {
98+ 100% {
99+ transform : rotate (360deg )
100+ }
101+ }
102+
103+ </style >
104+ <br ><br ><br >
105+ <details >
106+ <summary ><i class =" bi bi-caret-down-fill" ></i > Theme updater </summary >
107+ <div class =" content" style =" padding :10px ;" >
108+ <table >
109+ <tr >
110+ <th style =" width :85% ;" >Theme name:</th >
111+ <th style =" width : 15% ;" >Update status:</th >
112+ <th >Version:  ; </th >
113+ </tr >
114+ <?php
115+
116+ if ($handle = opendir (' themes' )) {
117+ while (false !== ($entry = readdir ($handle ))) {
118+
119+ if (file_exists (base_path (' themes' ) . ' /' . $entry . ' /readme.md' )){
120+ $text = file_get_contents (base_path (' themes' ) . ' /' . $entry . ' /readme.md' );
121+ $pattern = '/ Theme Version:.* /' ;
122+ preg_match ($pattern , $text , $matches , PREG_OFFSET_CAPTURE );
123+ if (sizeof ($matches ) > 0 ) {
124+ $verNr = substr ($matches [0 ][0 ],15 );
125+ }
126+ }
127+
128+ $themeVe = NULL ;
129+ if (! isset ($verNr )){$verNr = " error" ;};
130+
131+ if ($entry != " ." && $entry != " .." ) {
132+ echo ' <tr>' ;
133+ echo ' <th>' ; print_r (ucfirst ($entry ));
134+ echo ' </th>' ;
135+ echo ' <th><center>' ;
136+ if (file_exists (base_path (' themes' ) . ' /' . $entry . ' /readme.md' )){
137+ if (! strpos (file_get_contents (base_path (' themes' ) . ' /' . $entry . ' /readme.md' ), ' Source code:' )){$hasSource = false ;}else {
138+ $hasSource = true ;
139+
140+ $text = file_get_contents (base_path (' themes' ) . ' /' . $entry . ' /readme.md' );
141+ $pattern = '/ Source code:.* /' ;
142+ preg_match ($pattern , $text , $matches , PREG_OFFSET_CAPTURE );
143+ $sourceURL = substr ($matches [0 ][0 ],13 );
144+
145+ $replaced = str_replace (" https://github.com/" , " https://raw.githubusercontent.com/" , trim ($sourceURL ));
146+ $replaced = $replaced . " /main/readme.md" ;
147+
148+ if (strpos ($sourceURL , ' github.com' )){
149+
150+ ini_set (' user_agent' , ' Mozilla/4.0 (compatible; MSIE 6.0)' );
151+ try {
152+ $textGit = file_get_contents ($replaced );
153+ $patternGit = '/ Theme Version:.* /' ;
154+ preg_match ($patternGit , $textGit , $matches , PREG_OFFSET_CAPTURE );
155+ $sourceURLGit = substr ($matches [0 ][0 ],15 );
156+ $Vgitt = ' v' . $sourceURLGit ;
157+ $verNrv = ' v' . $verNr ;
158+ }catch (Exception $ex ){
159+ $themeVe = " error" ;
160+ $Vgitt = NULL ;
161+ $verNrv = NULL ;
162+ }
163+
164+ if (trim ($Vgitt ) > trim ($verNrv )){
165+ $updateAv = true ;
166+ $GLOBALS [' updateAv' ] = true ;
167+ } else {
168+ $updateAv = false ;
169+ }
170+ } else {$themeVe = " error" ;}
171+
172+ }
173+ }
174+
175+ if ($themeVe == " error" ) {
176+ echo ' <img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Error!&color=red">' ;
177+ } elseif ($hasSource == false ) {
178+ echo ' <a href="https://littlelink-custom.com/themes.php" target="_blank"><img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Update manually&color=red"></a>' ;
179+ } elseif ($updateAv == true ) {
180+ echo ' <img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Update available&color=yellow">' ;
181+ } else {
182+ echo ' <img style="scale:0.9" src="https://img.llc.ovh/static/v1?label=&message=Up to date&color=green">' ;
183+ }
184+ echo ' </center></th>' ;
185+ echo ' <th>' . $verNr . ' </th>' ;
186+ echo ' </tr>' ;}
187+ }} ? >
188+ </table >
189+ </div >
190+ <a href =" {{ url (' update/theme' )} }" onclick =" updateicon()" class =" mt-3 ml-3 btn btn-info row" ><span id =" updateicon" class =" " ><i class =" bi bi-arrow-repeat" ></i ></span > Update all themes</a ><br ><br >
191+ <script >
192+ function updateicon () {
193+ var element = document .getElementById (" updateicon" );
194+ element .classList .add (" updatespin" );
195+ }
196+
197+ </script >
198+ </details >
199+
200+ <?php
201+ try { if ($GLOBALS [' updateAv' ] == true ) echo ' <img style="padding-left:40px; padding-top:15px; scale: 1.5;" src="https://img.llc.ovh/static/v1?label=&message=A theme needs updating&color=brightgreen">' ;
202+ }catch (Exception $ex ){}
203+ ? >
204+
205+ <script >
206+ $ (function () {
207+ $ (' select[name=theme]' ).on (' change' , function () {
208+ var s = $ (this ).data (' base-url' ) + " ?t=" + $ (this ).val ();
209+ $ (" #frPreview" ).prop (' src' , s);
210+ })
211+ });
212+
213+ class Accordion {
214+ constructor (el ) {
215+ // Store the <details> element
216+ this .el = el;
217+ // Store the <summary> element
218+ this .summary = el .querySelector (' summary' );
219+ // Store the <div class="content"> element
220+ this .content = el .querySelector (' .content' );
221+
222+ // Store the animation object (so we can cancel it if needed)
223+ this .animation = null ;
224+ // Store if the element is closing
225+ this .isClosing = false ;
226+ // Store if the element is expanding
227+ this .isExpanding = false ;
228+ // Detect user clicks on the summary element
229+ this .summary .addEventListener (' click' , (e ) => this .onClick (e));
230+ }
231+
232+ onClick (e ) {
233+ // Stop default behaviour from the browser
234+ e .preventDefault ();
235+ // Add an overflow on the <details> to avoid content overflowing
236+ this .el .style .overflow = ' hidden' ;
237+ // Check if the element is being closed or is already closed
238+ if (this .isClosing || ! this .el .open ) {
239+ this .open ();
240+ // Check if the element is being openned or is already open
241+ } else if (this .isExpanding || this .el .open ) {
242+ this .shrink ();
243+ }
244+ }
245+
246+ shrink () {
247+ // Set the element as "being closed"
248+ this .isClosing = true ;
249+
250+ // Store the current height of the element
251+ const startHeight = ` ${ this .el .offsetHeight } px` ;
252+ // Calculate the height of the summary
253+ const endHeight = ` ${ this .summary .offsetHeight } px` ;
254+
255+ // If there is already an animation running
256+ if (this .animation ) {
257+ // Cancel the current animation
258+ this .animation .cancel ();
259+ }
260+
261+ // Start a WAAPI animation
262+ this .animation = this .el .animate ({
263+ // Set the keyframes from the startHeight to endHeight
264+ height: [startHeight, endHeight]
265+ }, {
266+ duration: 400
267+ , easing: ' ease-out'
268+ });
269+
270+ // When the animation is complete, call onAnimationFinish()
271+ this .animation .onfinish = () => this .onAnimationFinish (false );
272+ // If the animation is cancelled, isClosing variable is set to false
273+ this .animation .oncancel = () => this .isClosing = false ;
274+ }
275+
276+ open () {
277+ // Apply a fixed height on the element
278+ this .el .style .height = ` ${ this .el .offsetHeight } px` ;
279+ // Force the [open] attribute on the details element
280+ this .el .open = true ;
281+ // Wait for the next frame to call the expand function
282+ window .requestAnimationFrame (() => this .expand ());
283+ }
284+
285+ expand () {
286+ // Set the element as "being expanding"
287+ this .isExpanding = true ;
288+ // Get the current fixed height of the element
289+ const startHeight = ` ${ this .el .offsetHeight } px` ;
290+ // Calculate the open height of the element (summary height + content height)
291+ const endHeight = ` ${ this .summary .offsetHeight + this .content .offsetHeight } px` ;
292+
293+ // If there is already an animation running
294+ if (this .animation ) {
295+ // Cancel the current animation
296+ this .animation .cancel ();
297+ }
298+
299+ // Start a WAAPI animation
300+ this .animation = this .el .animate ({
301+ // Set the keyframes from the startHeight to endHeight
302+ height: [startHeight, endHeight]
303+ }, {
304+ duration: 400
305+ , easing: ' ease-out'
306+ });
307+ // When the animation is complete, call onAnimationFinish()
308+ this .animation .onfinish = () => this .onAnimationFinish (true );
309+ // If the animation is cancelled, isExpanding variable is set to false
310+ this .animation .oncancel = () => this .isExpanding = false ;
311+ }
312+
313+ onAnimationFinish (open ) {
314+ // Set the open attribute based on the parameter
315+ this .el .open = open;
316+ // Clear the stored animation
317+ this .animation = null ;
318+ // Reset isClosing & isExpanding
319+ this .isClosing = false ;
320+ this .isExpanding = false ;
321+ // Remove the overflow hidden and the fixed height
322+ this .el .style .height = this .el .style .overflow = ' ' ;
323+ }
324+ }
325+
326+ document .querySelectorAll (' details' ).forEach ((el ) => {
327+ new Accordion (el);
328+ });
329+
330+ </script >
331+
332+ @endif
333+ @endif
0 commit comments