chiark / gitweb /
Pesky carriage returns
[fdroidserver.git] / wp-fdroid / wp-fdroid.php
1 <?php
2 /*
3 Plugin Name: WP FDroid
4 Plugin URI: http://f-droid.org/repository
5 Description: An FDroid repository browser
6 Author: Ciaran Gultnieks
7 Version: 0.01
8 Author URI: http://ciarang.com
9
10 Revision history
11 0.01 - 2010-12-04: Initial development version
12
13 */
14
15 include('android-permissions.php');
16
17 class FDroid
18 {
19
20         // Our text domain, for internationalisation
21         private $textdom='wp-fdroid';
22
23         private $site_path;
24
25         // Constructor
26         function FDroid() {
27                 // Add filters etc here!
28                 add_shortcode('fdroidrepo',array($this, 'do_shortcode'));
29                 add_filter('query_vars',array($this, 'queryvars'));
30                 $this->inited=false;
31                 $this->site_path=getenv('DOCUMENT_ROOT');
32                 wp_register_sidebar_widget('fdroid_latest', 'FDroid Latest', 'widget_fdroidlatest');
33         }
34
35
36         // Register additional query variables. (Handler for the 'query_vars' filter)
37         function queryvars($qvars) {
38                 $qvars[]='fdfilter';
39                 $qvars[]='fdcategory';
40                 $qvars[]='fdid';
41                 $qvars[]='fdpage';
42                 $qvars[]='fdstyle';
43                 return $qvars;
44         }
45
46
47         // Lazy initialise. All non-trivial members should call this before doing anything else.
48         function lazyinit() {
49                 if(!$this->inited) {
50                         load_plugin_textdomain($this->textdom, PLUGINDIR.'/'.dirname(plugin_basename(__FILE__)), dirname(plugin_basename(__FILE__)));
51
52                         $this->inited=true;
53                 }
54         }
55
56         // Gets a required query parameter by name.
57         function getrequiredparam($name) {
58                 global $wp_query;
59                 if(!isset($wp_query->query_vars[$name]))
60                         wp_die("Missing parameter ".$name,"Error");
61                 return $wp_query->query_vars[$name];
62         }
63
64         // Handler for the 'fdroidrepo' shortcode.
65         //  $attribs - shortcode attributes
66         //  $content - optional content enclosed between the starting and
67         //                       ending shortcode
68         // Returns the generated content.
69         function do_shortcode($attribs,$content=null) {
70                 global $wp_query,$wp_rewrite;
71                 $this->lazyinit();
72
73                 // Init local query vars
74                 foreach($this->queryvars(array()) as $qv) {
75                         if(array_key_exists($qv,$wp_query->query_vars)) {
76                                 $query_vars[$qv] = $wp_query->query_vars[$qv];
77                         } else {
78                                 $query_vars[$qv] = null;
79                         }
80                 }
81
82                 // Santiy check query vars
83                 if(!isset($query_vars['fdpage']) || !is_numeric($query_vars['fdpage']) || $query_vars['fdpage'] <= 0) {
84                         $query_vars['fdpage'] = 1;
85                 }
86
87                 $out = '';
88
89                 if(isset($attribs['search']) && $query_vars['fdfilter']===null) {
90                         $query_vars['fdfilter'] = '';
91                 }
92
93                 if($query_vars['fdcategory'] == 'All applications') {
94                         unset($query_vars['fdcategory']);
95                 }
96                 
97                 if($query_vars['fdid']!==null) {
98                         $out.=$this->get_app($query_vars);
99                 } else {
100                         if($query_vars['fdfilter'] !== null) {
101                                 $out.='<form name="searchform" action="" method="get">';
102                                 $out.='<p><input name="fdfilter" type="text" value="'.$query_vars['fdfilter'].'" size="30"> ';
103                                 $out.='<input type="submit" value="Search"></p>';
104                                 $out.=$this->makeformdata($query_vars);
105                                 $out.='</form>'."\n";
106                         }
107
108                         $out.=$this->get_apps($query_vars);
109                 }
110
111                 return $out;
112         }
113
114
115         function get_app($query_vars) {
116                 global $permissions_data;
117                 $permissions_object = new AndroidPermissions($this->site_path.'/wp-content/plugins/wp-fdroid/AndroidManifest.xml',
118                         $this->site_path.'/wp-content/plugins/wp-fdroid/strings.xml',
119                         sys_get_temp_dir().'/android-permissions.cache');
120                 $permissions_data = $permissions_object->get_permissions_array();
121
122                 // Get app data
123                 $xml = simplexml_load_file($this->site_path.'/repo/index.xml');
124                 foreach($xml->children() as $app) {
125
126                         $attrs=$app->attributes();
127                         if($attrs['id']==$query_vars['fdid']) {
128                                 $apks=array();;
129                                 foreach($app->children() as $el) {
130                                         switch($el->getName()) {
131                                                 case "name":
132                                                         $name=$el;
133                                                         break;
134                                                 case "icon":
135                                                         $icon=$el;
136                                                         break;
137                                                 case "summary":
138                                                         $summary=$el;
139                                                         break;
140                                                 case "description":
141                                                         $desc=$el;
142                                                         break;
143                                                 case "license":
144                                                         $license=$el;
145                                                         break;
146                                                 case "source":
147                                                         $source=$el;
148                                                         break;
149                                                 case "tracker":
150                                                         $issues=$el;
151                                                         break;
152                                                 case "donate":
153                                                         $donate=$el;
154                                                         break;
155                                                 case "web":
156                                                         $web=$el;
157                                                         break;
158                                                 case "antifeatures";
159                                                         $antifeatures=$el;
160                                                         break;
161                                                 case "requirements";
162                                                         $requirements=$el;
163                                                         break;
164                                                 case "package":
165                                                         $thisapk=array();
166                                                         foreach($el->children() as $pel) {
167                                                                 switch($pel->getName()) {
168                                                                 case "version":
169                                                                         $thisapk['version']=$pel;
170                                                                         break;
171                                                                 case "vercode":
172                                                                         $thisapk['vercode']=$pel;
173                                                                         break;
174                                                                 case "apkname":
175                                                                         $thisapk['apkname']=$pel;
176                                                                         break;
177                                                                 case "srcname":
178                                                                         $thisapk['srcname']=$pel;
179                                                                         break;
180                                                                 case "hash":
181                                                                         $thisapk['hash']=$pel;
182                                                                         break;
183                                                                 case "size":
184                                                                         $thisapk['size']=$pel;
185                                                                         break;
186                                                                 case "sdkver":
187                                                                         $thisapk['sdkver']=$pel;
188                                                                         break;
189                                                                 case "permissions":
190                                                                         $thisapk['permissions']=$pel;
191                                                                         break;
192                                                                 }
193                                                         }
194                                                         $apks[]=$thisapk;
195
196                                         }
197                                 }
198
199                                 // Generate app diff data
200                                 foreach(array_reverse($apks, true) as $key=>$apk) {
201                                         if(isset($previous)) {
202                                                 // Apk size
203                                                 $apks[$key]['diff']['size'] = $apk['size']-$previous['size'];
204                                         }
205
206                                         // Permissions
207                                         $permissions = explode(',',$apk['permissions']);
208                                         $permissionsPrevious = isset($previous['permissions'])?explode(',',$previous['permissions']):array();
209                                         $apks[$key]['diff']['permissions']['added'] = array_diff($permissions, $permissionsPrevious);
210                                         $apks[$key]['diff']['permissions']['removed'] = array_diff($permissionsPrevious, $permissions);
211
212                                         $previous = $apk;
213                                 }
214
215                                 // Output app information
216                                 $out='<div id="appheader">';
217                                 $out.='<div style="float:left;padding-right:10px;"><img src="http://f-droid.org/repo/icons/'.$icon.'" width=48></div>';
218                                 $out.='<p><span style="font-size:20px">'.$name."</span>";
219                                 $out.="<br>".$summary."</p>";
220                                 $out.="</div>";
221
222                                 $out.="<p>".$desc."</p>";
223
224                                 if(isset($antifeatures)) {
225                                         $antifeaturesArray = explode(',',$antifeatures);
226                                         foreach($antifeaturesArray as $antifeature) {
227                                                 $antifeatureDesctiption = $this->get_antifeature_description($antifeature);
228                                                 $out.='<p style="border:3px solid #CC0000;background-color:#FFDDDD;padding:5px;"><strong>'.$antifeatureDesctiption['name'].'</strong><br />';
229                                                 $out.=$antifeatureDesctiption['description'].'</p>';
230                                         }
231                                 }
232
233                                 $out.="<p>";
234                                 $out.="<b>License:</b> ".$license;
235                                 if(isset($requirements)) {
236                                         $out.='<br /><b>Additional requirements:</b> '.$requirements;
237                                 }
238                                 $out.="</p>";
239
240                                 $out.="<p>";
241                                 if(strlen($web)>0)
242                                         $out.='<b>Website:</b> <a href="'.$web.'">'.$web.'</a><br />';
243                                 if(strlen($issues)>0)
244                                         $out.='<b>Issue Tracker:</b> <a href="'.$issues.'">'.$issues.'</a><br />';
245                                 if(strlen($source)>0)
246                                         $out.='<b>Source Code:</b> <a href="'.$source.'">'.$source.'</a><br />';
247                                 if($donate && strlen($donate)>0)
248                                         $out.='<b>Donate:</b> <a href="'.$donate.'">'.$donate.'</a><br />';
249                                 $out.="</p>";
250
251                                 $out.='<script type="text/javascript">';
252                                 $out.='function showHidePermissions(id) {';
253                                 $out.='  if(document.getElementById(id).style.display==\'none\')';
254                                 $out.=' document.getElementById(id).style.display=\'block\';';
255                                 $out.='  else';
256                                 $out.=' document.getElementById(id).style.display=\'none\';';
257                                 $out.='  return false;';
258                                 $out.='}';
259                                 $out.='</script>';
260
261                                 $out.="<h3>Packages</h3>";
262                                 $i=0;
263                                 foreach($apks as $apk) {
264                                         $first = $i+1==count($apks);
265                                         $out.="<p><b>Version ".$apk['version']."</b><br />";
266                                         $out.='<a href="https://f-droid.org/repo/'.$apk['apkname'].'">download apk</a> ';
267                                         $out.=$this->human_readable_size($apk['size']);
268                                         $diffSize = $apk['diff']['size'];
269                                         if(abs($diffSize) > 500) {
270                                                 $out.=' <span style="color:#AAAAAA;">(';
271                                                 $out.=$diffSize>0?'+':'';
272                                                 $out.=$this->human_readable_size($diffSize, 1).')</span>';
273                                         }
274                                         if(isset($apk['srcname']) && file_exists($this->site_path.'/repo/'.$apk['srcname'])) {
275                                                 $out.='<br /><a href="https://f-droid.org/repo/'.$apk['srcname'].'">source tarball</a> ';
276                                                 $out.=$this->human_readable_size(filesize($this->site_path.'/repo/'.$apk['srcname']));
277                                         }
278
279                                         if(isset($apk['permissions'])) {
280                                                 // Permissions diff link
281                                                 if($first == false) {
282                                                         $permissionsAddedCount = count($apk['diff']['permissions']['added']);
283                                                         $permissionsRemovedCount = count($apk['diff']['permissions']['removed']);
284                                                         $divIdDiff='permissionsDiff'.$i;
285                                                         if($permissionsAddedCount || $permissionsRemovedCount) {
286                                                                 $out.='<br /><a href="javascript:void(0);" onClick="showHidePermissions(\''.$divIdDiff.'\');">permissions diff</a>';
287                                                                 $out.=' <span style="color:#AAAAAA;">(';
288                                                                 if($permissionsAddedCount)
289                                                                         $out.='+'.$permissionsAddedCount;
290                                                                 if($permissionsAddedCount && $permissionsRemovedCount)
291                                                                         $out.='/';
292                                                                 if($permissionsRemovedCount)
293                                                                         $out.='-'.$permissionsRemovedCount;
294                                                                 $out.=')</span>';
295                                                         }
296                                                         else
297                                                         {
298                                                                 $out.='<br /><span style="color:#999999;">no permission changes</span>';
299                                                         }
300                                                 }
301
302                                                 // Permissions list link
303                                                 $permissionsListString = $this->get_permission_list_string(explode(',',$apk['permissions']), $permissions_data, $summary);
304                                                 /*if($i==0)
305                                                         $divStyleDisplay='block';
306                                                 else*/
307                                                         $divStyleDisplay='none';
308                                                 $divId='permissions'.$i;
309                                                 $out.='<br /><a href="javascript:void(0);" onClick="showHidePermissions(\''.$divId.'\');">view permissions</a>';
310                                                 $out.=' <span style="color:#AAAAAA;">['.$summary.']</span>';
311                                                 $out.='<br/>';
312
313                                                 // Permissions list
314                                                 $out.='<div style="display:'.$divStyleDisplay.';" id="'.$divId.'">';
315                                                 $out.=$permissionsListString;
316                                                 $out.='</div>';
317
318                                                 // Permissions diff
319                                                 {
320                                                         $out.='<div style="display:'.$divStyleDisplay.';" id="'.$divIdDiff.'">';
321                                                         $permissionsRemoved = $apk['diff']['permissions']['removed'];
322                                                         usort($permissionsRemoved, "permissions_cmp");
323
324                                                         // Added permissions
325                                                         if($permissionsAddedCount) {
326                                                                 $out.='<h5>ADDED</h5><br />';
327                                                                 $out.=$this->get_permission_list_string($apk['diff']['permissions']['added'], $permissions_data, $summary);
328                                                         }
329
330                                                         // Removed permissions
331                                                         if($permissionsRemovedCount) {
332                                                                 $out.='<h5>REMOVED</h5><br />';
333                                                                 $out.=$this->get_permission_list_string($apk['diff']['permissions']['removed'], $permissions_data, $summary);
334                                                         }
335
336                                                         $out.='</div>';
337                                                 }
338                                         }
339                                         else {
340                                                 $out.='<br /><span style="color:#999999;">no extra permissions needed</span><br />';
341                                         }
342
343                                         $out.='</p>';
344                                         $i++;
345                                 }
346
347                                 $out.='<hr><p><a href="'.makelink($query_vars,array('fdid'=>null)).'">Index</a></p>';
348
349                                 return $out;
350                         }
351                 }
352                 return "<p>Application not found</p>";
353         }
354
355         private function get_permission_list_string($permissions, $permissions_data, &$summary) {
356                 $out='';
357                 usort($permissions, "permissions_cmp");
358                 $permission_group_last = '';
359                 foreach($permissions as $permission) {
360                         $permission_group = $permissions_data['permission'][$permission]['permissionGroup'];
361                         if($permission_group != $permission_group_last) {
362                                 $permission_group_label = $permissions_data['permission-group'][$permission_group]['label'];
363                                 if($permission_group_label=='') $permission_group_label = 'Extra/Custom';
364                                 $out.='<strong>'.strtoupper($permission_group_label).'</strong><br/>';
365                                 $permission_group_last = $permission_group;
366                         }
367
368                         $out.=$this->get_permission_protection_level_icon($permissions_data['permission'][$permission]['protectionLevel']).' ';
369                         $out.='<strong>'.$permissions_data['permission'][$permission]['label'].'</strong> [<code>'.$permission.'</code>]<br/>';
370                         if($permissions_data['permission'][$permission]['description']) $out.=$permissions_data['permission'][$permission]['description'].'<br/>';
371                         //$out.=$permissions_data['permission'][$permission]['comment'].'<br/>';
372                         $out.='<br/>';
373
374                         if(!isset($summaryCount[$permissions_data['permission'][$permission]['protectionLevel']]))
375                                 $summaryCount[$permissions_data['permission'][$permission]['protectionLevel']] = 0;
376                         $summaryCount[$permissions_data['permission'][$permission]['protectionLevel']]++;
377                 }
378
379                 $summary = '';
380                 foreach($summaryCount as $protectionLevel => $count) {
381                         $summary .= $this->get_permission_protection_level_icon($protectionLevel, 'regular').' '.$count;
382                         $summary .= ', ';
383                 }
384                 $summary = substr($summary,0,-2);
385
386                 return $out;
387         }
388
389         private function get_permission_protection_level_icon($protection_level, $size='adjusted') {
390                 $iconString = '';
391                 if($protection_level=='dangerous') {
392                         $iconString .= '<span style="color:#DD9900;';
393                         if($size=='adjusted')
394                                 $iconString .= 'font-size:150%;';
395                         $iconString .= '">&#x26a0;</span>';     // WARNING SIGN
396                 }
397                 elseif($protection_level=='normal') {
398                         $iconString .= '<span style="color:#6666FF;';
399                         if($size=='adjusted')
400                                 $iconString .= 'font-size:110%;';
401                         $iconString .= '">&#x24D8;</span>';     // CIRCLED LATIN SMALL LETTER I
402                 }
403                 elseif($protection_level=='signature') {
404                         $iconString .= '<span style="color:#33AAAA;';
405                         if($size=='adjusted')
406                                 $iconString .= 'font-size:140%;';
407                         $iconString .= '">&#x273D;</span>';     // HEAVY TEARDROP-SPOKED ASTERISK
408                 }
409                 elseif($protection_level=='signatureOrSystem') {
410                         $iconString .= '<span style="color:#DD66DD;';
411                         if($size=='adjusted')
412                                 $iconString .= 'font-size:140%;';
413                         $iconString .= '">&#x269B;</span>';     // ATOM SYMBOL
414                 }
415                 else {
416                         $iconString .= '<span style="color:#33AA33';
417                         if($size=='adjusted')
418                                 $iconString .= ';font-size:130%;';
419                         $iconString .= '">&#x2699;</span>';     // GEAR
420                 }
421
422                 return $iconString;
423         }
424
425         private function human_readable_size($size, $minDiv=0) {
426                 $si_prefix = array('bytes','kB','MB');
427                 $div = 1000;
428
429                 for($i=0;(abs($size) > $div && $i < count($si_prefix)) || $i<$minDiv;$i++) {
430                         $size /= $div;
431                 }
432
433                 return round($size,max(0,$i-1)).' '.$si_prefix[$i];
434         }
435
436         private function get_antifeature_description($antifeature) {
437                 // Anti feature names and descriptions
438                 $antifeatureDesctiption['ads']['name'] = 'Advertising';
439                 $antifeatureDesctiption['ads']['description'] = 'This application contains advertising';
440                 $antifeatureDesctiption['tracking']['name'] = 'Tracks You';
441                 $antifeatureDesctiption['tracking']['description'] = 'This application tracks and reports your activity to somewhere';
442                 $antifeatureDesctiption['nonfreenet']['name'] = 'Non-Free Network Services';
443                 $antifeatureDesctiption['nonfreenet']['description'] = 'This application promotes a non-Free network service';
444                 $antifeatureDesctiption['nonfreeadd']['name'] = 'Non-Free Addons';
445                 $antifeatureDesctiption['nonfreeadd']['description'] = 'This application promotes non-Free add-ons';
446                 $antifeatureDesctiption['nonfreedep']['name'] = 'Non-Free Dependencies';
447                 $antifeatureDesctiption['nonfreedep']['description'] = 'This application depends on another non-Free application';
448
449                 $antifeatureLower = strtolower($antifeature);
450                 if(isset($antifeatureDesctiption[$antifeatureLower])) {
451                         return $antifeatureDesctiption[$antifeatureLower];
452                 }
453                 return array('name'=>$antifeature);
454         }
455
456
457         function get_apps($query_vars) {
458
459                 $xml = simplexml_load_file($this->site_path."/repo/index.xml");
460                 $matches = $this->show_apps($xml,$query_vars,$numpages);
461
462                 $out='';
463
464                 if(($query_vars['fdfilter']===null || $query_vars['fdfilter']!='') && $numpages>0)
465                 {
466                         $out.='<div style="float:left;">';
467                         if($query_vars['fdfilter']===null) {
468
469                                 $categories = array('All categories');
470                                 $handle = fopen(getenv('DOCUMENT_ROOT').'/repo/categories.txt', 'r');
471                                 if ($handle) {
472                                         while (($buffer = fgets($handle, 4096)) !== false) {
473                                             $categories[] = rtrim($buffer);
474                                         }
475                                     fclose($handle);
476                                 }
477
478                                 $out.='<form name="categoryform" action="" method="get">';
479                                 $out.=$this->makeformdata($query_vars);
480                                 
481                                 $out.='<select name="fdcategory" style="color:#333333;" onChange="document.categoryform.submit();">';
482                                 foreach($categories as $category) {
483                                         $out.='<option';
484                                         if(isset($query_vars['fdcategory']) && $category==$query_vars['fdcategory'])
485                                                 $out.=' selected';
486                                         $out.='>'.$category.'</option>';
487                                 }
488                                 $out.='</select>';
489                                 
490                                 $out.='</form>'."\n";
491                         }
492                         else {
493                                 $out.='Applications matching "'.$query_vars['fdfilter'].'"';
494                         }
495                         $out.="</div>";
496
497                         $out.='<div style="float:right;">';
498                         $out.='<a href="'.makelink($query_vars, array('fdstyle'=>'list','fdpage'=>'1')).'">List</a> | ';
499                         $out.='<a href="'.makelink($query_vars, array('fdstyle'=>'grid','fdpage'=>'1')).'">Grid</a>';
500                         $out.='</div>';
501
502                         $out.='<br break="all"/>';
503                 }
504
505                 if($numpages>0) {
506                         $out.=$matches;
507
508                         $out.='<hr><p>';
509
510                         $out.='<div style="width:20%; float:left; text-align:left;">';
511                         $out.=' Page '.$query_vars['fdpage'].' of '.$numpages.' ';
512                         $out.='</div>';
513
514                         $out.='<div style="width:60%; float:left; text-align:center;">';
515                         if($numpages>1) {
516                                 for($i=1;$i<=$numpages;$i++) {
517                                         if($i == $query_vars['fdpage']) {
518                                                 $out.='<b>'.$i.'</b>';
519                                         } else {
520                                                 $out.='<a href="'.makelink($query_vars, array('fdpage'=>$i)).'">';
521                                                 $out.=$i;
522                                                 $out.='</a>';
523                                         }
524                                         $out.=' ';
525                                 }
526                                 $out.=' ';
527                         }
528                         $out.='</div>';
529
530                         $out.='<div style="width:20%; float:left; text-align:right;">';
531                         if($query_vars['fdpage']!=$numpages) {
532                                 $out.='<a href="'.makelink($query_vars, array('fdpage'=>($query_vars['fdpage']+1))).'">next&gt;</a> ';
533                         }
534                         $out.='</div>';
535
536                         $out.='</p>';
537                 } else if($query_vars['fdfilter']!='') {
538                         $out.='<p>No matches</p>';
539                 }
540
541                 return $out;
542         }
543
544
545         function makeformdata($query_vars) {
546
547                 $out='';
548
549                 $out.='<input type="hidden" name="page_id" value="'.get_query_var('page_id').'">';
550                 foreach($query_vars as $name => $value) {
551                         if($value !== null && $name != 'fdfilter')
552                                 $out.='<input type="hidden" name="'.$name.'" value="'.$value.'">';
553                 }
554
555                 return $out;
556         }
557
558
559         function show_apps($xml,$query_vars,&$numpages) {
560
561                 $skipped=0;
562                 $got=0;
563                 $total=0;
564
565                 if($query_vars['fdstyle']=='grid') {
566                         $outputter = new FDOutGrid();
567                 } else {
568                         $outputter = new FDOutList();
569                 }
570
571                 $out = "";
572
573                 $out.=$outputter->outputStart();
574
575                 foreach($xml->children() as $app) {
576
577                         if($app->getName() == 'repo') continue;
578                         $appinfo['attrs']=$app->attributes();
579                         $appinfo['id']=$appinfo['attrs']['id'];
580                         foreach($app->children() as $el) {
581                                 switch($el->getName()) {
582                                         case "name":
583                                                 $appinfo['name']=$el;
584                                                 break;
585                                         case "icon":
586                                                 $appinfo['icon']=$el;
587                                                 break;
588                                         case "summary":
589                                                 $appinfo['summary']=$el;
590                                                 break;
591                                         case "description":
592                                                 $appinfo['description']=$el;
593                                                 break;
594                                         case "license":
595                                                 $appinfo['license']=$el;
596                                                 break;
597                                         case "category":
598                                                 $appinfo['category']=$el;
599                                                 break;
600                                 }
601                         }
602
603                         if(($query_vars['fdfilter']===null || $query_vars['fdfilter']!='' && (stristr($appinfo['name'],$query_vars['fdfilter']) || stristr($appinfo['summary'],$query_vars['fdfilter']) || stristr($appinfo['description'],$query_vars['fdfilter']))) && (!isset($query_vars['fdcategory']) || $query_vars['fdcategory'] && $query_vars['fdcategory']==$appinfo['category'])) {
604                                 if($skipped<($query_vars['fdpage']-1)*$outputter->perpage) {
605                                         $skipped++;
606                                 } else if($got<$outputter->perpage) {
607                                         $out.=$outputter->outputEntry($query_vars, $appinfo);
608                                         $got++;
609                                 }
610                                 $total++;
611                         }
612
613                 }
614
615                 $out.=$outputter->outputEnd();
616
617                 $numpages = ceil((float)$total/$outputter->perpage);
618
619                 return $out;
620         }
621 }
622
623 // Class to output app entries in a detailed list format
624 class FDOutList
625 {
626         var $perpage=30;
627
628         function FDOutList() {
629         }
630
631         function outputStart() {
632                 return '';
633         }
634
635         function outputEntry($query_vars, $appinfo) {
636                 $out="";
637                 $out.='<hr style="clear:both;" />'."\n";
638                 $out.='<a href="'.makelink($query_vars, array('fdid'=>$appinfo['id'])).'">';
639                 $out.='<div id="appheader">';
640
641                 $out.='<div style="float:left;padding-right:10px;"><img src="http://f-droid.org/repo/icons/'.$appinfo['icon'].'" style="width:48px;border:none;"></div>';
642
643                 $out.='<div style="float:right;">';
644                 $out.='<p>Details...</p>';
645                 $out.="</div>\n";
646
647                 $out.='<p style="color:#000000;"><span style="font-size:20px;">'.$appinfo['name']."</span>";
648                 $out.="<br>".$appinfo['summary']."</p>\n";
649
650                 $out.="</div>\n";
651                 $out.='</a>';
652
653                 return $out;
654         }
655
656         function outputEnd() {
657                 return '';
658         }
659 }
660
661 // Class to output app entries in a compact grid format
662 class FDOutGrid
663 {
664         var $perpage=80;
665
666         var $itemCount = 0;
667
668         function FDOutGrid() {
669         }
670
671         function outputStart() {
672                 return "\n".'<table border="0" width="100%"><tr>'."\n";
673         }
674
675         function outputEntry($query_vars, $appinfo) {
676                 $link=makelink($query_vars, array('fdid'=>$appinfo['id']));
677
678                 $out='';
679
680                 if($this->itemCount%4 == 0 && $this->itemCount > 0)
681                 {
682                         $out.='</tr><tr>'."\n";
683                 }
684
685                 $out.='<td align="center" valign="top" style="background-color:#F8F8F8;">';
686                 $out.='<p>';
687                 $out.='<div id="appheader" style="text-align:center;width:110px;">';
688
689                 $out.='<a href="'.$link.'" style="border-bottom-style:none;">';
690                 $out.='<img src="http://f-droid.org/repo/icons/'.$appinfo['icon'].'" style="width:48px;border-width:0;padding-top:5px;padding-bottom:5px;"><br/>';
691                 $out.=$appinfo['name'].'<br/>';
692                 $out.='</a>';
693
694                 $out.="</div>";
695                 $out.='</p>';
696                 $out.="</td>\n";
697
698                 $this->itemCount++;
699                 return $out;
700         }
701
702         function outputEnd() {
703                 return '</tr></table>'."\n";
704         }
705 }
706
707 function permissions_cmp($a, $b) {
708         global $permissions_data;
709
710         $aProtectionLevel = $permissions_data['permission'][$a]['protectionLevel'];
711         $bProtectionLevel = $permissions_data['permission'][$b]['protectionLevel'];
712
713         if($aProtectionLevel != $bProtectionLevel) {
714                 if(strlen($aProtectionLevel)==0) return 1;
715                 if(strlen($bProtectionLevel)==0) return -1;
716
717                 return strcmp($aProtectionLevel, $bProtectionLevel);
718         }
719
720         $aGroup = $permissions_data['permission'][$a]['permissionGroup'];
721         $bGroup = $permissions_data['permission'][$b]['permissionGroup'];
722
723         if($aGroup != $bGroup) {
724                 return strcmp($aGroup, $bGroup);
725         }
726
727         return strcmp($a, $b);
728 }
729
730 // Make a link to this page, with the current query vars attached and desired params added/modified
731 function makelink($query_vars, $params=array()) {
732         $link=get_permalink();
733         $vars=linkify(array_merge($query_vars, $params));
734         if(strlen($vars)==0)
735                 return $link;
736         if(strpos($link,'?')===false)
737                 $link.='?';
738         else
739                 $link.='&';
740         return $link.$vars;
741 }
742
743 // Return the key value pairs in http-get-parameter format as a string
744 function linkify($vars) {
745         $retvar = '';
746         foreach($vars as $k => $v) {
747                 if($k!==null && $v!==null && $v!='')
748                         $retvar .= $k.'='.$v.'&';
749         }
750         return substr($retvar,0,-1);
751 }
752
753 function widget_fdroidlatest($args) {
754         extract($args);
755         echo $before_widget;
756         echo $before_title . 'Latest Apps' . $after_title;
757         
758         $handle = fopen(getenv('DOCUMENT_ROOT').'/repo/latestapps.dat', 'r');
759         if ($handle) {
760                 while (($buffer = fgets($handle, 4096)) !== false) {
761                         $app = explode("\t", $buffer);
762                         echo '<a href="/repository/browse/?fdid='.$app[0].'">';
763                         if(isset($app[2]) && trim($app[2])) {
764                                 echo '<img src="http://f-droid.org/repo/icons/'.$app[2].'" style="width:32px;border:none;float:right;" />';
765                         }
766                         echo $app[1].'<br />';
767                         if(isset($app[3]) && trim($app[3])) {
768                                 echo '<span style="color:#BBBBBB;">'.$app[3].'</span>';
769                         }
770                         echo '</a><br style="clear:both;" />';
771                 }
772                 fclose($handle);
773         }
774         
775         echo $after_widget;
776 }
777
778 $wp_fdroid = new FDroid();
779
780
781 ?>