<commit_before><?php
/*
Plugin Name: WP FDroid
Plugin URI: http://f-droid.org/repository
Description: An FDroid repository browser
Author: <NAME>
Version: 0.01
Author URI: http://ciarang.com

Revision history
0.01 - 2010-12-04: Initial development version

*/

include('android-permissions.php');

class FDroid
{

	// Our text domain, for internationalisation
	private $textdom='wp-fdroid';

	private $site_path;

	// Constructor
	function FDroid() {
		// Add filters etc here!
		add_shortcode('fdroidrepo',array($this, 'do_shortcode'));
		add_filter('query_vars',array($this, 'queryvars'));
		$this->inited=false;
		$this->site_path=getenv('DOCUMENT_ROOT');
	}


	// Register additional query variables. (Handler for the 'query_vars' filter)
	function queryvars($qvars) {
		$qvars[]='fdfilter';
		$qvars[]='fdid';
		$qvars[]='fdpage';
		$qvars[]='fdstyle';
		return $qvars;
	}


	// Lazy initialise. All non-trivial members should call this before doing anything else.
	function lazyinit() {
		if(!$this->inited) {
			load_plugin_textdomain($this->textdom, PLUGINDIR.'/'.dirname(plugin_basename(__FILE__)), dirname(plugin_basename(__FILE__)));

			$this->inited=true;
		}
	}

	// Gets a required query parameter by name.
	function getrequiredparam($name) {
		global $wp_query;
		if(!isset($wp_query->query_vars[$name]))
			wp_die("Missing parameter ".$name,"Error");
		return $wp_query->query_vars[$name];
	}

	// Handler for the 'fdroidrepo' shortcode.
	//  $attribs - shortcode attributes
	//  $content - optional content enclosed between the starting and
	//			 ending shortcode
	// Returns the generated content.
	function do_shortcode($attribs,$content=null) {
		global $wp_query,$wp_rewrite;
		$this->lazyinit();

		// Init local query vars
		foreach($this->queryvars(array()) as $qv) {
			if(array_key_exists($qv,$wp_query->query_vars)) {
				$query_vars[$qv] = $wp_query->query_vars[$qv];
			} else {
				$query_vars[$qv] = null;
			}
		}

		// Santiy check query vars
		if(!isset($query_vars['fdpage']) || !is_numeric($query_vars['fdpage']) || $query_vars['fdpage'] <= 0) {
			$query_vars['fdpage'] = 1;
		}

		$out = '';

		if(isset($attribs['search']) && $query_vars['fdfilter']===null) {
			$query_vars['fdfilter'] = '';
		}

		if($query_vars['fdid']!==null) {
			$out.=$this->get_app($query_vars);
		} else {
			if($query_vars['fdfilter'] !== null)
				$out.=$this->show_search($query_vars);

			$out.=$this->get_apps($query_vars);
		}

		return $out;
	}


	function get_app($query_vars) {
		global $permissions_data;
		$permissions_object = new AndroidPermissions($this->site_path.'/wp-content/plugins/wp-fdroid/AndroidManifest.xml',
			$this->site_path.'/wp-content/plugins/wp-fdroid/strings.xml',
			sys_get_temp_dir().'/android-permissions.cache');
		$permissions_data = $permissions_object->get_permissions_array();

		// Get app data
		$xml = simplexml_load_file($this->site_path.'/repo/index.xml');
		foreach($xml->children() as $app) {

			$attrs=$app->attributes();
			if($attrs['id']==$query_vars['fdid']) {
				$apks=array();;
				foreach($app->children() as $el) {
					switch($el->getName()) {
						case "name":
							$name=$el;
							break;
						case "icon":
							$icon=$el;
							break;
						case "summary":
							$summary=$el;
							break;
						case "description":
							$desc=$el;
							break;
						case "license":
							$license=$el;
							break;
						case "source":
							$source=$el;
							break;
						case "tracker":
							$issues=$el;
							break;
						case "donate":
							$donate=$el;
							break;
						case "web":
							$web=$el;
							break;
						case "package":
							$thisapk=array();
							foreach($el->children() as $pel) {
								switch($pel->getName()) {
								case "version":
									$thisapk['version']=$pel;
									break;
								case "vercode":
									$thisapk['vercode']=$pel;
									break;
								case "apkname":
									$thisapk['apkname']=$pel;
									break;
								case "srcname":
									$thisapk['srcname']=$pel;
									break;
								case "hash":
									$thisapk['hash']=$pel;
									break;
								case "size":
									$thisapk['size']=$pel;
									break;
								case "sdkver":
									$thisapk['sdkver']=$pel;
									break;
								case "permissions":
									$thisapk['permissions']=$pel;
									break;
								}
							}
							$apks[]=$thisapk;

					}
				}

				// Generate app diff data
				foreach(array_reverse($apks, true) as $key=>$apk) {
					if(isset($previous)) {
						// Apk size
						$apks[$key]['diff']['size'] = $apk['size']-$previous['size'];
					}

					// Permissions
					$permissions = explode(',',$apk['permissions']);
					$permissionsPrevious = isset($previous['permissions'])?explode(',',$previous['permissions']):array();
					$apks[$key]['diff']['permissions']['added'] = array_diff($permissions, $permissionsPrevious);
					$apks[$key]['diff']['permissions']['removed'] = array_diff($permissionsPrevious, $permissions);

					$previous = $apk;
				}

				// Output app information
				$out='<div id="appheader">';
				$out.='<div style="float:left;padding-right:10px;"><img src="http://f-droid.org/repo/icons/'.$icon.'" width=48></div>';
				$out.='<p><span style="font-size:20px">'.$name."</span>";
				$out.="<br>".$summary."</p>";
				$out.="</div>";

				$out.="<p>".$desc."</p>";

				$out.="<p><b>License:</b> ".$license."</p>";

				$out.="<p>";
				if(strlen($web)>0)
					$out.='<b>Website:</b> <a href="'.$web.'">'.$web.'</a><br />';
				if(strlen($issues)>0)
					$out.='<b>Issue Tracker:</b> <a href="'.$issues.'">'.$issues.'</a><br />';
				if(strlen($source)>0)
					$out.='<b>Source Code:</b> <a href="'.$source.'">'.$source.'</a><br />';
				if($donate && strlen($donate)>0)
					$out.='<b>Donate:</b> <a href="'.$donate.'">'.$donate.'</a><br />';
				$out.="</p>";

				$out.='<script type="text/javascript">';
				$out.='function showHidePermissions(id) {';
				$out.='  if(document.getElementById(id).style.display==\'none\')';
				$out.='	document.getElementById(id).style.display=\'block\';';
				$out.='  else';
				$out.='	document.getElementById(id).style.display=\'none\';';
				$out.='  return false;';
				$out.='}';
				$out.='</script>';

				$out.="<h3>Packages</h3>";
				$i=0;
				foreach($apks as $apk) {
					$first = $i+1==count($apks);
					$out.="<p><b>Version ".$apk['version']."</b><br />";
					$out.='<a href="http://f-droid.org/repo/'.$apk['apkname'].'">download apk</a> ';
					$out.=$this->human_readable_size($apk['size']);
					$diffSize = $apk['diff']['size'];
					if(abs($diffSize) > 500) {
						$out.=' <span style="color:#AAAAAA;">(';
						$out.=$diffSize>0?'+':'';
						$out.=$this->human_readable_size($diffSize, 1).')</span>';
					}
					if(isset($apk['srcname']) && file_exists($this->site_path.'/repo/'.$apk['srcname'])) {
						$out.='<br /><a href="http://f-droid.org/repo/'.$apk['srcname'].'">source tarball</a> ';
						$out.=$this->human_readable_size(filesize($this->site_path.'/repo/'.$apk['srcname']));
					}

					if(isset($apk['permissions'])) {
						// Permissions diff link
						if($first == false) {
							$permissionsAddedCount = count($apk['diff']['permissions']['added']);
							$permissionsRemovedCount = count($apk['diff']['permissions']['removed']);
							$divIdDiff='permissionsDiff'.$i;
							if($permissionsAddedCount || $permissionsRemovedCount) {
								$out.='<br /><a href="javascript:void(0);" onClick="showHidePermissions(\''.$divIdDiff.'\');">permissions diff</a>';
								$out.=' <span style="color:#AAAAAA;">(';
								if($permissionsAddedCount)
									$out.='+'.$permissionsAddedCount;
								if($permissionsAddedCount && $permissionsRemovedCount)
									$out.='/';
								if($permissionsRemovedCount)
									$out.='-'.$permissionsRemovedCount;
								$out.=')</span>';
							}
							else
							{
								$out.='<br /><span style="color:#999999;">no permission changes</span>';
							}
						}

						// Permissions list link
						$permissionsListString = $this->get_permission_list_string(explode(',',$apk['permissions']), $permissions_data, $summary);
						/*if($i==0)
							$divStyleDisplay='block';
						else*/
							$divStyleDisplay='none';
						$divId='permissions'.$i;
						$out.='<br /><a href="javascript:void(0);" onClick="showHidePermissions(\''.$divId.'\');">view permissions</a>';
						$out.=' <span style="color:#AAAAAA;">['.$summary.']</span>';
						$out.='<br/>';

						// Permissions list
						$out.='<div style="display:'.$divStyleDisplay.';" id="'.$divId.'">';
						$out.=$permissionsListString;
						$out.='</div>';

						// Permissions diff
						{
							$out.='<div style="display:'.$divStyleDisplay.';" id="'.$divIdDiff.'">';
							$permissionsRemoved = $apk['diff']['permissions']['removed'];
							usort($permissionsRemoved, "permissions_cmp");

							// Added permissions
							if($permissionsAddedCount) {
								$out.='<h5>ADDED</h5><br />';
								$out.=$this->get_permission_list_string($apk['diff']['permissions']['added'], $permissions_data, $summary);
							}

							// Removed permissions
							if($permissionsRemovedCount) {
								$out.='<h5>REMOVED</h5><br />';
								$out.=$this->get_permission_list_string($apk['diff']['permissions']['removed'], $permissions_data, $summary);
							}

							$out.='</div>';
						}
					}
					else {
						$out.='<br /><span style="color:#999999;">no extra permissions needed</span><br />';
					}

					$out.='</p>';
					$i++;
				}

				$out.='<hr><p><a href="'.makelink($query_vars,array('fdid'=>null)).'">Index</a></p>';

				return $out;
			}
		}
		return "<p>Application not found</p>";
	}

	private function get_permission_list_string($permissions, $permissions_data, &$summary) {
		$out='';
		usort($permissions, "permissions_cmp");
		$permission_group_last = '';
		foreach($permissions as $permission) {
			$permission_group = $permissions_data['permission'][$permission]['permissionGroup'];
			if($permission_group != $permission_group_last) {
				$permission_group_label = $permissions_data['permission-group'][$permission_group]['label'];
				if($permission_group_label=='') $permission_group_label = 'Extra/Custom';
				$out.='<strong>'.strtoupper($permission_group_label).'</strong><br/>';
				$permission_group_last = $permission_group;
			}

			$out.=$this->get_permission_protection_level_icon($permissions_data['permission'][$permission]['protectionLevel']).' ';
			$out.='<strong>'.$permissions_data['permission'][$permission]['label'].'</strong> [<code>'.$permission.'</code>]<br/>';
			if($permissions_data['permission'][$permission]['description']) $out.=$permissions_data['permission'][$permission]['description'].'<br/>';
			//$out.=$permissions_data['permission'][$permission]['comment'].'<br/>';
			$out.='<br/>';

			if(!isset($summaryCount[$permissions_data['permission'][$permission]['protectionLevel']]))
				$summaryCount[$permissions_data['permission'][$permission]['protectionLevel']] = 0;
			$summaryCount[$permissions_data['permission'][$permission]['protectionLevel']]++;
		}

		$summary = '';
		foreach($summaryCount as $protectionLevel => $count) {
			$summary .= $this->get_permission_protection_level_icon($protectionLevel, 'regular').' '.$count;
			$summary .= ', ';
		}
		$summary = substr($summary,0,-2);

		return $out;
	}

	private function get_permission_protection_level_icon($protection_level, $size='adjusted') {
		$iconString = '';
		if($protection_level=='dangerous') {
			$iconString .= '<span style="color:#DD9900;';
			if($size=='adjusted')
				$iconString .= 'font-size:150%;';
			$iconString .= '">&#x26a0;</span>';
		}
		elseif($protection_level=='normal') {
			$iconString .= '<span style="color:#6666FF;';
			if($size=='adjusted')
				$iconString .= 'font-size:110%;';
			$iconString .= '">&#x24D8;</span>';
		}
		else {
			$iconString .= '<span style="color:#33AA33';
			if($size=='adjusted')
				$iconString .= ';font-size:130%;';
			$iconString .= '">&#x2699;</span>';
		}

		return $iconString;
	}

	private function human_readable_size($size, $minDiv=0) {
		$si_prefix = array('bytes','kB','MB');
		$div = 1000;

		for($i=0;(abs($size) > $div && $i < count($si_prefix)) || $i<$minDiv;$i++) {
			$size /= $div;
		}

		return round($size,max(0,$i-1)).' '.$si_prefix[$i];
	}

	function get_apps($query_vars) {

		$xml = simplexml_load_file($this->site_path."/repo/index.xml");
		$matches = $this->show_apps($xml,$query_vars,$numpages);
<commit_msg>Added a notice for apps with anti-features.<commit_after><?php
/*
Plugin Name: WP FDroid
Plugin URI: http://f-droid.org/repository
Description: An FDroid repository browser
Author: <NAME>
Version: 0.01
Author URI: http://ciarang.com

Revision history
0.01 - 2010-12-04: Initial development version

*/

include('android-permissions.php');

class FDroid
{

	// Our text domain, for internationalisation
	private $textdom='wp-fdroid';

	private $site_path;

	// Constructor
	function FDroid() {
		// Add filters etc here!
		add_shortcode('fdroidrepo',array($this, 'do_shortcode'));
		add_filter('query_vars',array($this, 'queryvars'));
		$this->inited=false;
		$this->site_path=getenv('DOCUMENT_ROOT');
	}


	// Register additional query variables. (Handler for the 'query_vars' filter)
	function queryvars($qvars) {
		$qvars[]='fdfilter';
		$qvars[]='fdid';
		$qvars[]='fdpage';
		$qvars[]='fdstyle';
		return $qvars;
	}


	// Lazy initialise. All non-trivial members should call this before doing anything else.
	function lazyinit() {
		if(!$this->inited) {
			load_plugin_textdomain($this->textdom, PLUGINDIR.'/'.dirname(plugin_basename(__FILE__)), dirname(plugin_basename(__FILE__)));

			$this->inited=true;
		}
	}

	// Gets a required query parameter by name.
	function getrequiredparam($name) {
		global $wp_query;
		if(!isset($wp_query->query_vars[$name]))
			wp_die("Missing parameter ".$name,"Error");
		return $wp_query->query_vars[$name];
	}

	// Handler for the 'fdroidrepo' shortcode.
	//  $attribs - shortcode attributes
	//  $content - optional content enclosed between the starting and
	//			 ending shortcode
	// Returns the generated content.
	function do_shortcode($attribs,$content=null) {
		global $wp_query,$wp_rewrite;
		$this->lazyinit();

		// Init local query vars
		foreach($this->queryvars(array()) as $qv) {
			if(array_key_exists($qv,$wp_query->query_vars)) {
				$query_vars[$qv] = $wp_query->query_vars[$qv];
			} else {
				$query_vars[$qv] = null;
			}
		}

		// Santiy check query vars
		if(!isset($query_vars['fdpage']) || !is_numeric($query_vars['fdpage']) || $query_vars['fdpage'] <= 0) {
			$query_vars['fdpage'] = 1;
		}

		$out = '';

		if(isset($attribs['search']) && $query_vars['fdfilter']===null) {
			$query_vars['fdfilter'] = '';
		}

		if($query_vars['fdid']!==null) {
			$out.=$this->get_app($query_vars);
		} else {
			if($query_vars['fdfilter'] !== null)
				$out.=$this->show_search($query_vars);

			$out.=$this->get_apps($query_vars);
		}

		return $out;
	}


	function get_app($query_vars) {
		global $permissions_data;
		$permissions_object = new AndroidPermissions($this->site_path.'/wp-content/plugins/wp-fdroid/AndroidManifest.xml',
			$this->site_path.'/wp-content/plugins/wp-fdroid/strings.xml',
			sys_get_temp_dir().'/android-permissions.cache');
		$permissions_data = $permissions_object->get_permissions_array();

		// Get app data
		$xml = simplexml_load_file($this->site_path.'/repo/index.xml');
		foreach($xml->children() as $app) {

			$attrs=$app->attributes();
			if($attrs['id']==$query_vars['fdid']) {
				$apks=array();;
				foreach($app->children() as $el) {
					switch($el->getName()) {
						case "name":
							$name=$el;
							break;
						case "icon":
							$icon=$el;
							break;
						case "summary":
							$summary=$el;
							break;
						case "description":
							$desc=$el;
							break;
						case "license":
							$license=$el;
							break;
						case "source":
							$source=$el;
							break;
						case "tracker":
							$issues=$el;
							break;
						case "donate":
							$donate=$el;
							break;
						case "web":
							$web=$el;
							break;
						case "antifeatures";
							$antifeatures=$el;
						case "package":
							$thisapk=array();
							foreach($el->children() as $pel) {
								switch($pel->getName()) {
								case "version":
									$thisapk['version']=$pel;
									break;
								case "vercode":
									$thisapk['vercode']=$pel;
									break;
								case "apkname":
									$thisapk['apkname']=$pel;
									break;
								case "srcname":
									$thisapk['srcname']=$pel;
									break;
								case "hash":
									$thisapk['hash']=$pel;
									break;
								case "size":
									$thisapk['size']=$pel;
									break;
								case "sdkver":
									$thisapk['sdkver']=$pel;
									break;
								case "permissions":
									$thisapk['permissions']=$pel;
									break;
								}
							}
							$apks[]=$thisapk;

					}
				}

				// Generate app diff data
				foreach(array_reverse($apks, true) as $key=>$apk) {
					if(isset($previous)) {
						// Apk size
						$apks[$key]['diff']['size'] = $apk['size']-$previous['size'];
					}

					// Permissions
					$permissions = explode(',',$apk['permissions']);
					$permissionsPrevious = isset($previous['permissions'])?explode(',',$previous['permissions']):array();
					$apks[$key]['diff']['permissions']['added'] = array_diff($permissions, $permissionsPrevious);
					$apks[$key]['diff']['permissions']['removed'] = array_diff($permissionsPrevious, $permissions);

					$previous = $apk;
				}

				// Output app information
				$out='<div id="appheader">';
				$out.='<div style="float:left;padding-right:10px;"><img src="http://f-droid.org/repo/icons/'.$icon.'" width=48></div>';
				$out.='<p><span style="font-size:20px">'.$name."</span>";
				$out.="<br>".$summary."</p>";
				$out.="</div>";

				$out.="<p>".$desc."</p>";

				if(isset($antifeatures)) {
					$antifeaturesArray = explode(',',$antifeatures);
					foreach($antifeaturesArray as $antifeature) {
						$antifeatureDesctiption = $this->get_antifeature_description($antifeature);
						$out.='<p style="border:3px solid #CC0000;background-color:#FFDDDD;padding:5px;"><strong>'.$antifeatureDesctiption['name'].'</strong><br />';
						$out.=$antifeatureDesctiption['description'].'</p>';
					}
				}

				$out.="<p><b>License:</b> ".$license."</p>";

				$out.="<p>";
				if(strlen($web)>0)
					$out.='<b>Website:</b> <a href="'.$web.'">'.$web.'</a><br />';
				if(strlen($issues)>0)
					$out.='<b>Issue Tracker:</b> <a href="'.$issues.'">'.$issues.'</a><br />';
				if(strlen($source)>0)
					$out.='<b>Source Code:</b> <a href="'.$source.'">'.$source.'</a><br />';
				if($donate && strlen($donate)>0)
					$out.='<b>Donate:</b> <a href="'.$donate.'">'.$donate.'</a><br />';
				$out.="</p>";

				$out.='<script type="text/javascript">';
				$out.='function showHidePermissions(id) {';
				$out.='  if(document.getElementById(id).style.display==\'none\')';
				$out.='	document.getElementById(id).style.display=\'block\';';
				$out.='  else';
				$out.='	document.getElementById(id).style.display=\'none\';';
				$out.='  return false;';
				$out.='}';
				$out.='</script>';

				$out.="<h3>Packages</h3>";
				$i=0;
				foreach($apks as $apk) {
					$first = $i+1==count($apks);
					$out.="<p><b>Version ".$apk['version']."</b><br />";
					$out.='<a href="http://f-droid.org/repo/'.$apk['apkname'].'">download apk</a> ';
					$out.=$this->human_readable_size($apk['size']);
					$diffSize = $apk['diff']['size'];
					if(abs($diffSize) > 500) {
						$out.=' <span style="color:#AAAAAA;">(';
						$out.=$diffSize>0?'+':'';
						$out.=$this->human_readable_size($diffSize, 1).')</span>';
					}
					if(isset($apk['srcname']) && file_exists($this->site_path.'/repo/'.$apk['srcname'])) {
						$out.='<br /><a href="http://f-droid.org/repo/'.$apk['srcname'].'">source tarball</a> ';
						$out.=$this->human_readable_size(filesize($this->site_path.'/repo/'.$apk['srcname']));
					}

					if(isset($apk['permissions'])) {
						// Permissions diff link
						if($first == false) {
							$permissionsAddedCount = count($apk['diff']['permissions']['added']);
							$permissionsRemovedCount = count($apk['diff']['permissions']['removed']);
							$divIdDiff='permissionsDiff'.$i;
							if($permissionsAddedCount || $permissionsRemovedCount) {
								$out.='<br /><a href="javascript:void(0);" onClick="showHidePermissions(\''.$divIdDiff.'\');">permissions diff</a>';
								$out.=' <span style="color:#AAAAAA;">(';
								if($permissionsAddedCount)
									$out.='+'.$permissionsAddedCount;
								if($permissionsAddedCount && $permissionsRemovedCount)
									$out.='/';
								if($permissionsRemovedCount)
									$out.='-'.$permissionsRemovedCount;
								$out.=')</span>';
							}
							else
							{
								$out.='<br /><span style="color:#999999;">no permission changes</span>';
							}
						}

						// Permissions list link
						$permissionsListString = $this->get_permission_list_string(explode(',',$apk['permissions']), $permissions_data, $summary);
						/*if($i==0)
							$divStyleDisplay='block';
						else*/
							$divStyleDisplay='none';
						$divId='permissions'.$i;
						$out.='<br /><a href="javascript:void(0);" onClick="showHidePermissions(\''.$divId.'\');">view permissions</a>';
						$out.=' <span style="color:#AAAAAA;">['.$summary.']</span>';
						$out.='<br/>';

						// Permissions list
						$out.='<div style="display:'.$divStyleDisplay.';" id="'.$divId.'">';
						$out.=$permissionsListString;
						$out.='</div>';

						// Permissions diff
						{
							$out.='<div style="display:'.$divStyleDisplay.';" id="'.$divIdDiff.'">';
							$permissionsRemoved = $apk['diff']['permissions']['removed'];
							usort($permissionsRemoved, "permissions_cmp");

							// Added permissions
							if($permissionsAddedCount) {
								$out.='<h5>ADDED</h5><br />';
								$out.=$this->get_permission_list_string($apk['diff']['permissions']['added'], $permissions_data, $summary);
							}

							// Removed permissions
							if($permissionsRemovedCount) {
								$out.='<h5>REMOVED</h5><br />';
								$out.=$this->get_permission_list_string($apk['diff']['permissions']['removed'], $permissions_data, $summary);
							}

							$out.='</div>';
						}
					}
					else {
						$out.='<br /><span style="color:#999999;">no extra permissions needed</span><br />';
					}

					$out.='</p>';
					$i++;
				}

				$out.='<hr><p><a href="'.makelink($query_vars,array('fdid'=>null)).'">Index</a></p>';

				return $out;
			}
		}
		return "<p>Application not found</p>";
	}

	private function get_permission_list_string($permissions, $permissions_data, &$summary) {
		$out='';
		usort($permissions, "permissions_cmp");
		$permission_group_last = '';
		foreach($permissions as $permission) {
			$permission_group = $permissions_data['permission'][$permission]['permissionGroup'];
			if($permission_group != $permission_group_last) {
				$permission_group_label = $permissions_data['permission-group'][$permission_group]['label'];
				if($permission_group_label=='') $permission_group_label = 'Extra/Custom';
				$out.='<strong>'.strtoupper($permission_group_label).'</strong><br/>';
				$permission_group_last = $permission_group;
			}

			$out.=$this->get_permission_protection_level_icon($permissions_data['permission'][$permission]['protectionLevel']).' ';
			$out.='<strong>'.$permissions_data['permission'][$permission]['label'].'</strong> [<code>'.$permission.'</code>]<br/>';
			if($permissions_data['permission'][$permission]['description']) $out.=$permissions_data['permission'][$permission]['description'].'<br/>';
			//$out.=$permissions_data['permission'][$permission]['comment'].'<br/>';
			$out.='<br/>';

			if(!isset($summaryCount[$permissions_data['permission'][$permission]['protectionLevel']]))
				$summaryCount[$permissions_data['permission'][$permission]['protectionLevel']] = 0;
			$summaryCount[$permissions_data['permission'][$permission]['protectionLevel']]++;
		}

		$summary = '';
		foreach($summaryCount as $protectionLevel => $count) {
			$summary .= $this->get_permission_protection_level_icon($protectionLevel, 'regular').' '.$count;
			$summary .= ', ';
		}
		$summary = substr($summary,0,-2);

		return $out;
	}

	private function get_permission_protection_level_icon($protection_level, $size='adjusted') {
		$iconString = '';
		if($protection_level=='dangerous') {
			$iconString .= '<span style="color:#DD9900;';
			if($size=='adjusted')
				$iconString .= 'font-size:150%;';
			$iconString .= '">&#x26a0;</span>';
		}
		elseif($protection_level=='normal') {
			$iconString .= '<span style="color:#6666FF;';
			if($size=='adjusted')
				$iconString .= 'font-size:110%;';
			$iconString .= '">&#x24D8;</span>';
		}
		else {
			$iconString .= '<span style="color:#33AA33';
			if($size=='adjusted')
				$iconString .= ';font-size:130%;';
			$iconString .= '">&#x2699;</span>';
		}

		return $iconString;
	}

	private function human_readable_size($size, $minDiv=0) {
		$si_prefix = array('bytes','kB','MB');
		$div = 1000;

		for($i=0;(abs($size) > $div && $i < count($si_prefix)) || $i<$minDiv;$i++) {
			$size /= $div;
		}

		return round($size,max(0,$i-1)).' '.$si_prefix[$i];
	}

	private function get_antifeature_description($antifeature) {
		// Anti feature names and descriptions
		$antifeatureDesctiption['ads']['name'] = 'Advertising';
		$antifeatureDesctiption['ads']['description'] = 'This application contains advertising';
		$antifeatureDesctiption['tracking']['name'] = 'Tracks You';
		$antifeatureDesctiption['tracking']['description'] = 'This application tracks and reports your activity to somewhere';
		$antifeatureDesctiption['nonfreenet']['name'] = 'Non-Free Network Services';
		$antifeatureDesctiption['nonfreenet']['description'] = 'This application promotes a non-Free network service';
		$antifeatureDesctiption['nonfreeadd']['name'] = 'Non-Free Addons';
		$antifeatureDesctiption['nonfreeadd']['description'] = 'This application promotes non-Free add-ons';
		$antifeatureDesctiption['nonfreedep']['name'] = 'Non-Free Dependencies';
		$antifeatureDesctiption['nonfreedep']['description'] = 'This application depends on another non-Free application';

		return $antifeatureDesctiption[strtolower($antifeature)];
	}


	function get_apps($query_vars) {

		$xml = simplexml_load_file($this->site_path."/repo/index.xml");
		$matches = $this->show_apps($xml,$query_vars,$numpages);

{'fraction_non_alphanumeric': 0.21926685001695362, 'fraction_numerical': 0.0074595938665561546, 'mean_word_length': 3.716417910447761, 'pattern_counts': {'":': 36, '<': 230, '<?xml version=': 0, '>': 315, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 20}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6852684', 'n_tokens_mistral': 11593, 'n_tokens_neox': 8985, 'n_words': 1266}