diff options
Diffstat (limited to 'site/app/controllers')
25 files changed, 2237 insertions, 3577 deletions
diff --git a/site/app/controllers/addons_controller.php b/site/app/controllers/addons_controller.php index 5d7e1a3..385d231 100644 --- a/site/app/controllers/addons_controller.php +++ b/site/app/controllers/addons_controller.php @@ -46,10 +46,10 @@ class AddonsController extends AppController { var $name = 'Addons'; - var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox', 'checkAdvancedSearch'); - var $uses = array('Addon', 'AddonCollection', 'AddonTag', 'Addontype', 'Application', - 'Feature', 'File', 'GlobalStat', 'License', 'Platform', 'Preview', 'Tag', 'Translation', - 'Review', 'Version', 'Collection', 'CollectionPromo'); + var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox'); + var $uses = array('Addon', 'AddonTag', 'Addontype', 'Application', + 'Feature', 'File', 'Platform', 'Preview', 'Tag', 'Translation', + 'Review', 'Version'); var $components = array('Amo', 'Image', 'Pagination', 'Session', 'Userfunc'); var $helpers = array('Html', 'Link', 'Time', 'Localization', 'Ajax', 'Number', 'Pagination'); var $namedArgs = true; @@ -67,13 +67,13 @@ class AddonsController extends AppController redirectWithNewLocaleAndExit(array('addon',$this->params['url']['addons-author-addons-select'])); } - // Set of available link sharing services with associated labels and + // Set of available link sharing services with associated labels and // submission URL templates. // @TODO: Move this to a model class when share counts are enabled in DB - $this->link_sharing_services = array( + $this->link_sharing_services = array( // see: http://digg.com/tools/integrate#3 - 'digg' => array( + 'digg' => array( 'label' => ___('addons_share_label_digg', 'Digg this!'), 'url' => 'http://digg.com/submit?url={URL}&title={TITLE}&bodytext={DESCRIPTION}&media=news&topic=tech_news' ), @@ -112,14 +112,15 @@ class AddonsController extends AppController */ function share($id = null) { global $valid_status; - + global $app_listedtypes; + $_conditions = array( 'Addon.id' => $id, 'Addon.inactive' => 0, - 'Addon.addontype_id' => array( - ADDON_EXTENSION, ADDON_THEME, ADDON_DICT, + 'Addon.addontype_id' => array_merge(array( + ADDON_EXTENSION, ADDON_THEME, ADDON_DICT, ADDON_SEARCH, ADDON_LPAPP, ADDON_PLUGIN - ), + ), $app_listedtypes[APP_SUGAR]), 'Addon.status' => $valid_status ); $addon = $this->Addon->find($_conditions, null , null , 1); @@ -134,17 +135,17 @@ class AddonsController extends AppController $this->publish('addon_id', $id); - // Build a suitable link title based on the addon name, version, and + // Build a suitable link title based on the addon name, version, and // the site title. - $title = + $title = sprintf( - _('addons_display_pagetitle'), + _('addons_display_pagetitle'), $addon['Translation']['name']['string'].' '. $addon['Version'][0]['version'] - ) . + ) . ' :: '. sprintf( - _('addons_home_pagetitle'), + _('addons_home_pagetitle'), APP_PRETTYNAME ); @@ -157,24 +158,21 @@ class AddonsController extends AppController $this->layout = 'ajax'; } - + /** * Display an addon * @param int $id the id of the addon */ function display($id = null) { global $valid_status; - $this->publish('jsAdd', array('jquery-ui/ui.lightbox')); - $this->publish('cssAdd', array('jquery-lightbox')); - $this->layout = 'amo2009'; - $this->set('bodyclass', 'inverse'); - - $this->forceShadowDb(); + $this->Amo->clean($id); $this->publish('bigHeader', true); $this->publish('bigHeaderText', sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); - + + $this->layout='mozilla'; + $loggedIn = $this->Session->check('User')? true : false; $this->set('loggedIn', $loggedIn); @@ -182,7 +180,7 @@ class AddonsController extends AppController $this->flash(sprintf(_('error_missing_argument'), 'addon_id'), '/', 3); return; } - + $_conditions = array( 'Addon.id' => $id, 'Addon.inactive' => 0, @@ -190,58 +188,23 @@ class AddonsController extends AppController 'Addon.status' => $valid_status ); - $this->Addon->bindOnly('User', 'Version', 'Tag', 'AddonTag'); - $addon_data = $this->Addon->find($_conditions, null , null , 1); + global $app_listedtypes; + $_conditions['Addon.addontype_id'] = array_merge($_conditions['Addon.addontype_id'], $app_listedtypes[APP_SUGAR]); + $addon_data = $this->Addon->find($_conditions, null , null , 1); + if (empty($addon_data)) { $this->flash(_('error_addon_notfound'), '/', 3); return; } - // sandbox check - if ($addon_data['Addon']['status'] != STATUS_PUBLIC) { - $this->publish('addonStatus', STATUS_SANDBOX); - $this->status = $valid_status; - } - - $_latest_version = $this->Version->getVersionByAddonId($addon_data['Addon']['id'], $this->status); - if ($_latest_version > 0) { - $version = $this->Version->findAllById($_latest_version, null, "Version.created DESC", 0); - $addon_data['Version'] = $version; - $this->publish('hasversion', true); - $compat_apps = $this->Version->getCompatibleApps($_latest_version); - $this->publish('compatible_apps', array_slice($compat_apps, 0, 1)); - $addon_data['compatible_apps'] = $compat_apps; - } else { - $this->publish('hasversion', false); - $this->publish('compatible_apps', array()); - } - - // if the latest version is incompatible with the current app, redirect - // to a the first valid, compatible version. - if (!empty($compat_apps)) { - $is_compatible = false; - foreach ($compat_apps as $app) { - if ($app['Application']['application_id'] == APP_ID) { - $is_compatible = true; - break; - } - } - if (!$is_compatible) { - global $app_shortnames; - $targetapp = array_search($compat_apps[0]['Application']['application_id'], $app_shortnames); - $this->redirect("/{$targetapp}/addon/{$id}", null, true, false); - return; - } - } - // TODO: Look up the current share totals for this addon. // $share_counts = $this->ShareCount->getTotalCountsForAddon( // $addon_data['Addon']['id'] // ); // $this->set('link_sharing_counts', $share_counts); - // Not using publish() here because this will all be app constants, + // Not using publish() here because this will all be app constants, // localized strings with placeholders, or counts from the DB. $this->set('link_sharing_services', $this->link_sharing_services); @@ -252,7 +215,7 @@ class AddonsController extends AppController else { $this->publish('isAuthor', false); } - + // get other addons for the author(s) foreach ($addon_data['User'] as $_user) $_userids[] = $_user['id']; @@ -274,6 +237,24 @@ class AddonsController extends AppController if (in_array($addon_data['Addon']['addontype_id'], array(ADDON_PLUGIN))) { $this->redirect('/browse/type:' . $addon_data['Addon']['addontype_id']); } + + // sandbox check + if ($addon_data['Addon']['status'] != STATUS_PUBLIC) { + $this->publish('addonStatus', STATUS_SANDBOX); + $this->status = $valid_status; + } + + $_latest_version = $this->Version->getVersionByAddonId($addon_data['Addon']['id'], $this->status); + if ($_latest_version > 0) { + $version = $this->Version->findAllById($_latest_version, null, "Version.created DESC", 0); + $addon_data['Version'] = $version; + $this->publish('hasversion', true); + $compat_apps = $this->Version->getCompatibleApps($_latest_version); + $this->publish('compatible_apps', array_slice($compat_apps, 0, 1)); + } else { + $this->publish('hasversion', false); + $this->publish('compatible_apps', array()); + } $this->publish('previews', $this->Preview->findAllByAddonId($id, array('id', 'addon_id', 'caption'), 'highlight desc')); @@ -281,7 +262,7 @@ class AddonsController extends AppController $this->publish('addonIconPath', $this->Image->getAddonIconURL($id), false); $this->publish('addonPreviewPath', $this->Image->getHighlightedPreviewURL($id)); $this->pageTitle = sprintf(_('addons_display_pagetitle'), $addon_data['Translation']['name']['string']). ' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - + // get the tags that are related to the addon, so that they have translation data $_related_tag_ids = array(); foreach ($addon_data['Tag'] as $tagvalue){ @@ -300,12 +281,12 @@ class AddonsController extends AppController $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); - + // Grab the latest 3 reviews by ID, one per user. $review_ids = array(); - $_latest_reviews = + $_latest_reviews = $this->Review->findLatestReviewsForAddon($addon_data['Addon']['id'], 3, 1); - foreach($_latest_reviews as $_r) + foreach($_latest_reviews as $_r) $review_ids[] = $_r['id']; // Fetch the actual reviews from IDs @@ -315,8 +296,9 @@ class AddonsController extends AppController $reviews = array(); $this->publish('reviews', $reviews); - $this->publish('review_count', empty($reviews) ? 0 : $addon_data['Addon']['totalreviews']); - + $this->publish('review_count', empty($reviews) ? + 0 : $this->Review->countLatestReviewsForAddon($addon_data['Addon']['id'])); + // does user have a review already? $user = $this->Session->read('User'); if (!empty($user) && $_latest_version > 0) { @@ -325,253 +307,83 @@ class AddonsController extends AppController } else { $this->publish('hasreview', false); } - - // find collections this add-on is in - $pop_collections = array(); - $_pop_coll_ids = $this->AddonCollection->getPopularCollectionsForAddon($id, 3, APP_ID); - if (!empty($_pop_coll_ids)) { - $pop_collections = $this->Collection->findAllById($_pop_coll_ids, - array('id', 'uuid', 'nickname', 'name'), - 'FIELD(Collection.id,'.implode(',', $_pop_coll_ids).')', null, null, -1); - } - $this->publish('pop_collections', $pop_collections); - $collection_count = $this->AddonCollection->getCollectionCountForAddon($id, APP_ID); - $this->publish('collection_count', $collection_count, false); - - // Fetch user's collections if logged in - $userCollections = false; - if (!empty($user)) { - $userCollectionIds = $this->User->getCollections($user['id'], APP_ID, array($id)); - $userCollections = $this->Collection->findAll(array( - 'Collection.id' => $userCollectionIds, - 'Collection.collection_type' => '<> '.Collection::COLLECTION_TYPE_AUTOPUBLISHER - ), array('id', 'name', 'uuid', 'nickname')); - $this->publish('userCollections', $userCollections); - - // if an addon was just added to a collection, display a success message - if (false !== ($coll_uuid = $this->Session->read('collection_addon_added'))) { - $collection_id = $this->Collection->getIdForUUID($coll_uuid); - $coll_addon_added = $this->Collection->findById($collection_id, null, null, -1); - $this->Session->delete('collection_addon_added'); - $this->publish('coll_addon_added', $coll_addon_added); - } - } - + // Collapse categories menu $this->publish('collapse_categories', true); } - + + /** * Display the home page for the entire site. */ function home() { $this->forceShadowDb(); - - $this->layout='amo2009'; + global $valid_status, $app_listedtypes; + + $this->layout='mozilla'; $this->pageTitle = sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - - $this->publish('stats_downloaded', - $this->GlobalStat->getNamedCount('addons_downloaded')); - $this->publish('stats_inuse', - $this->GlobalStat->getNamedCount('addons_in_use')); - - $this->publish('teaser_collection_promos', $this->_findTeaserCollections()); - $this->publish('teaser_collections_categories', $this->CollectionPromo->titles_and_taglines); - $this->publish('popular_collections', $this->_findPopularCollections()); - $this->publish('promoted_collections', $this->_findCollectionPromoList(true)); - - list($featured_type, $featured_addons) = $this->_getFeatured(); - $this->publish('featured_type', $featured_type); - $this->publish('featured_addons', $featured_addons); - + + $feature_ids = $this->Addon->getRecommendedAddons(7); + $featureAddons = $this->Addon->getAddonList($feature_ids, array( + 'all_tags', 'authors', 'compatible_apps', 'files', 'latest_version', + 'list_details')); + $this->publish('featureAddons', $featureAddons); + unset($feature_ids, $featureAddons); + + // Get recommended and popular lists. + $list_num = 4; + $associations = array('single_tag'); + + $rec_addon_ids = $this->Addon->getRecommendedAddons($list_num); + $rec_addons = $this->Addon->getAddonList($rec_addon_ids, $associations); + $pop_addon_ids = $this->Addon->getAddonsFromCategory(STATUS_PUBLIC, + $app_listedtypes[APP_ID], 'all', 'popular', 'DESC', $list_num, 1, '', false); + $pop_addons = $this->Addon->getAddonList($pop_addon_ids, $associations); + $new_addon_ids = $this->Addon->getAddonsFromCategory(STATUS_PUBLIC, + $app_listedtypes[APP_ID], 'all', 'newest', 'DESC', $list_num, 1, '', false); + $new_addons = $this->Addon->getAddonList($new_addon_ids, $associations); + $upd_addon_ids = $this->Addon->getAddonsFromCategory(STATUS_PUBLIC, + $app_listedtypes[APP_ID], 'all', 'updated', 'DESC', $list_num, 1, '', false); + $upd_addons = $this->Addon->getAddonList($upd_addon_ids, $associations); + unset($list_num, $rec_addon_ids, $pop_addon_ids, $new_addon_ids, $upd_addon_ids); + + $this->publish('recAddons', $rec_addons); + $this->publish('popAddons', $pop_addons); + $this->publish('newAddons', $new_addons); + $this->publish('updAddons', $upd_addons); + // The platforms section is necessary because of CakePHP bug #1183 // (https://trac.cakephp.org/ticket/1183). We need the translated // strings in the model to offer the right platform to users. $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); - + $this->publish('baseurl', $this->base); $this->publish('bigHeader', true); $this->publish('bigHeaderText', sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); - + // add rss links to global feeds + // @partial translation fallback, 5/13/08 $this->publish('rssAdd', array( array('/browse/type:1/cat:all/format:rss?sort=newest', _('rss_newestaddons')), array('/browse/type:1/cat:all/format:rss?sort=updated', ___('rss_updatedaddons', 'Updated Add-ons')), - array('/browse/type:1/cat:all/format:rss?sort=popular', ___('rss_popularaddons', 'Popular Add-ons')), array('/recommended/format:rss', _('rss_featuredaddons')), - )); - } - - /* Used to populate the homepage addons by xhr. */ - function ajaxy() { - list($featured_type, $featured_addons) = $this->_getFeatured(); - $this->publish('featured_type', $featured_type); - $this->publish('featured_addons', $featured_addons); - $this->Platform->unbindFully(); - $platforms = $this->Platform->findAll(); - $this->publish('platforms', $platforms); - } - - function _getFeatured() { - global $app_listedtypes; - - $associations = array( - 'single_tag', 'all_tags', 'authors', 'compatible_apps', 'files', - 'latest_version', 'list_details' - ); - $list_num = 5; - - $featured_type = isset($_GET['featured']) ? - $_GET['featured'] : 'recommended'; - switch ($featured_type) { - case 'popular': - $featured_addon_ids = $this->Addon->getAddonsFromCategory( - STATUS_PUBLIC, $app_listedtypes[APP_ID], 'all', 'popular', - 'DESC', $list_num, 1, '', false - ); - break; - case 'added': - $featured_addon_ids = $this->Addon->getAddonsFromCategory( - STATUS_PUBLIC, $app_listedtypes[APP_ID], 'all', 'newest', - 'DESC', $list_num, 1, '', false - ); - break; - case 'updated': - $featured_addon_ids = $this->Addon->getAddonsFromCategory( - STATUS_PUBLIC, $app_listedtypes[APP_ID], 'all', 'updated', - 'DESC', $list_num, 1, '', false - ); - break; - case 'recommended': - default: - $featured_addon_ids = $this->Addon->getRecommendedAddons($list_num); - break; - } - $featured_addons = $this->Addon->getAddonList($featured_addon_ids, $associations); - - return array($featured_type, $featured_addons); - } - - /** - * Assemble collections for the teaser section of the home page. - * - * @return array - */ - function _findTeaserCollections() { - - if (APP_ID != APP_FIREFOX) { - - // FYF collections for teaser are only appropriate for Firefox. - return array(); - - } else { - $promoCatList = $this->_findCollectionPromoList(); - $teaser_collections = array(); - - $associations = array( - 'single_tag', 'all_tags', 'authors', 'compatible_apps', 'files', - 'latest_version', 'list_details' - ); - - foreach($promoCatList as $index => $collectionId) { - $addons = $this->Addon->getAddonsFromCollection($collectionId, 'RAND()', null, 3); - $teaser_collections[] = $this->Addon->getAddonList($addons, $associations); - } - - return $teaser_collections; - } - - } - - /** - * Assemble list of collections promoted per promotion category - * - * @return array - **/ - function _findCollectionPromoList($bindFully = false) { - $collectionPromos = $this->CollectionPromo->findAll(); - $promoCats = $this->CollectionPromo->titles_and_taglines; - $promoCatList = array(); - - //Doing this to merge collection ids into promotion categories and allow locale-specific selections to override - for($i = 0; $i < count($promoCats); $i++) { - $collection = false; - - if(isset($collectionPromos[LANG][$i])) { - $collection = $collectionPromos[LANG][$i]; - } elseif(isset($collectionPromos['all'][$i])) { - $collection = $collectionPromos['all'][$i]; - } - - if($collection) { - $id = array_keys($collection); - $id = $id[0]; - - if($bindFully) { - $promoCatList[] = $this->Collection->findById($id); - } else { - $promoCatList[] = $id; - } - } - } - - return $promoCatList; - } - - - - - - /** - * Fetch top 5 popular collections by subscriber count, return a - * stripped-down data structure for use by view. - * - * @return array - */ - function _findPopularCollections() { - - // Unbind and re-bind with just users and addons, then look up top 5 - // popular collections in descending order. - $this->Collection->unbindFully(); - $this->Collection->bindModel(array( - 'hasAndBelongsToMany' => array( - 'Users' => $this->Collection->hasAndBelongsToMany_full['Users'], - 'Addon' => $this->Collection->hasAndBelongsToMany_full['Addon'] - ) - )); - $collections = $this->Collection->findAll(array( - 'Collection.listed' => 1, - 'Collection.application_id' => APP_ID - ), null, 'Collection.subscribers DESC', 5); - - // Reduce the results from the model to the minimal set needed - // by the view, because escaping is expensive. - $pop_collections = array(); - foreach ($collections as $c) { - $authors = array(); - foreach ($c['Users'] as $u) { - $authors[] = array( - 'id' => $u['id'], - 'firstname' => $u['firstname'], - 'lastname' => $u['lastname'], - 'nickname' => $u['nickname'] - ); - } - $pop_collections[] = array( - 'uuid' => $c['Collection']['uuid'], - 'nickname' => $c['Collection']['nickname'], - 'authors' => $authors, - 'icon_url' => $this->Image->getCollectionIconURL($c['Collection']['id']), - 'name' => $c['Translation']['name']['string'], - 'description' => $c['Translation']['description']['string'], - 'subscribers' => $c['Collection']['subscribers'], - 'addons_count' => count($c['Addon']) - ); + )); + + // User name for Welcome message + if ($session = $this->Session->read('User')) { + if (!empty($session['firstname'])) + $welcomeName = $session['firstname']; + elseif (!empty($session['nickname'])) + $welcomeName = $session['nickname']; + elseif (!empty($session['lastname'])) + $welcomeName = $session['lastname']; + else + $welcomeName = ''; + + $this->publish('welcomeName', $welcomeName); } - - return $pop_collections; } /** @@ -581,7 +393,7 @@ class AddonsController extends AppController global $valid_status; // Get the type of addon, defaulting to themes - $addontype = isset($this->namedArgs['type']) ? + $addontype = isset($this->namedArgs['type']) ? $this->namedArgs['type'] : ADDON_THEME; // Get the addon category, defaulting to 'all' @@ -590,7 +402,7 @@ class AddonsController extends AppController $this->Tag->unbindFully(); $this_tag = $this->Tag->findById($category); - + // show experimental add-ons? if (isset($this->params['url']['exp'])) { /* experimental add-ons requested */ @@ -629,7 +441,7 @@ class AddonsController extends AppController $sort_by = $this->namedArgs['sort']; else $sort_by = ''; - + $allowed_sort_by = array( 'name', 'updated', 'newest', 'popular', 'rated' ); @@ -657,7 +469,7 @@ class AddonsController extends AppController list($_order,$_limit,$_page) = $this->Pagination->init(); $addons = $this->Addon->getAddonsByCategory( - null, $displaystatuses, $addontype, $category, + null, $displaystatuses, $addontype, $category, $sort_by, $sort_dir, $_limit, $_page, '', true ); @@ -670,16 +482,16 @@ class AddonsController extends AppController $this->publish('subcats', $subcats); $this->publish('all_total', $all_total); $this->publish('subcat_totals', $subcat_totals); - + $format = (isset($this->namedArgs['format']) ? $this->namedArgs['format'] : 'html'); - + $this->set('content_wide', true); // display 2 features next to each other - + $this->publish('collapse_categories', true); - + switch($addontype) { - case ADDON_THEME: - $this->pageTitle = sprintf(___('addons_browse_categories_header_theme'), $this_tag['Translation']['name']['string'], APP_PRETTYNAME); + case ADDON_THEME: + $this->pageTitle = sprintf(___('addons_browse_categories_header_theme'), $this_tag['Translation']['name']['string'], APP_PRETTYNAME); break; default: $this->pageTitle = sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); @@ -687,7 +499,7 @@ class AddonsController extends AppController $this->publish('bigHeader', true); $this->publish('bigHeaderText', sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); - + $this->layout = 'mozilla'; $this->render('browse_thumbs'); @@ -698,8 +510,7 @@ class AddonsController extends AppController */ function browse() { global $app_listedtypes, $hybrid_categories; - - $this->forceShadowDb(); + if (!isset($this->namedArgs['type'])) { // @TODO throw a 404 error $this->redirect('/'); @@ -717,7 +528,7 @@ class AddonsController extends AppController $this->redirect('/'); break; } - + if (!isset($this->namedArgs['cat'])) { switch ($this->namedArgs['type']) { case ADDON_SEARCH: @@ -729,7 +540,7 @@ class AddonsController extends AppController case ADDON_PLUGIN: // undeleted this -cpollett $this->_plugins(); break; - + case ADDON_DICT: $this->_dictionaries(); break; @@ -743,7 +554,7 @@ class AddonsController extends AppController } return; } - + /* redirect category hybrids to respective addontype page * (unless we have selected a full listing page (?sort=something)) */ $cat = $this->namedArgs['cat']; @@ -752,7 +563,7 @@ class AddonsController extends AppController $this->redirect("/browse/type:{$hybrid_categories[APP_ID][$cat]}"); return; } - + /* display generic cat landing page or full category listing page */ if (isset($_GET['sort']) || isset($this->namedArgs['sort'])) { @@ -769,47 +580,6 @@ class AddonsController extends AppController } } - function _buildMinimalAddonDetails($addons) { - $view_data = array(); - - foreach ($addons as $addon) { - $addon_id = $addon['Addon']['id']; - - $r = array( - 'icon_url' => - $this->Image->getAddonIconURL($addon_id), - 'preview_url' => - $this->Image->getHighlightedPreviewURL($addon_id), - 'version' => - $addon['Version'][0]['version'] - ); - - $a_fields = array( - 'id','guid','averagerating','created','modified', - 'weeklydownloads','totaldownloads' - ); - foreach ($a_fields as $field) - $r[$field] = $addon['Addon'][$field]; - - $t_fields = array('name','summary','description'); - foreach ($t_fields as $field) - $r[$field] = $addon['Translation'][$field]['string']; - - $u_fields = array('id', 'firstname', 'lastname', 'nickname'); - $r['authors'] = array(); - if (!empty($addon['User'])) foreach ($addon['User'] as $idx=>$user) { - foreach ($u_fields as $field) - $r['authors'][$idx][$field] = $user[$field]; - } - - $r['latestversion'] = $addon['Version'][0]['version']; - - $view_data[] = $r; - } - - return $view_data; - } - /** * Generic landing page for a specific add-on category * @@ -818,85 +588,61 @@ class AddonsController extends AppController * @access private */ function _categoryLanding() { - global $valid_status, $app_listedtypes; - + global $valid_status; + $valid_status = array(STATUS_PUBLIC); $format = $this->setLayoutForFormat(); - + $addontype = $this->namedArgs['type']; $this->Amo->clean($addontype); - $this->publish('type_id', $addontype); - + $category = $this->namedArgs['cat']; $this->Amo->clean($category); - $this->publish('cat_id', $category); - $this->Tag->unbindFully(); $this_tag = $this->Tag->findById($category); - $this->publish('this_tag', $this_tag); - - // Build a minimal set of addon details for publishing to view. - $_feat_ids = $this->AddonTag->getRandomAddons($category, true, 6, null, $addontype=='all' ? null : $addontype); - if (count($_feat_ids) > 0) { - $_order_by = 'FIELD(Addon.id, '.implode(',', $_feat_ids).')'; + + // fetch 5 recommended add-ons for slider + $_feat_ids = $this->AddonTag->getRandomAddons($category, true, 5, null, $addontype=='all' ? null : $addontype); + if (!empty($_feat_ids)) { + $featureAddons = $this->Addon->getListAddons($_feat_ids, $valid_status, + 'FIELD(Addon.id,'.implode(',', $_feat_ids).')', true); } else { - $_order_by = 'Addon.id'; + $featureAddons = false; } - $featureAddons = $this->Addon->getListAddons($_feat_ids, $valid_status, $_order_by, true); - - $this->publish('featured_addons', $featureAddons); - - $list_num = 10; - - $pop_addon_ids = $this->Addon->getAddonsFromCategory( - STATUS_PUBLIC, $app_listedtypes[APP_ID], $category, - 'popular', 'DESC', $list_num, 1, '', false - ); - $pop_addons = $this->_buildMinimalAddonDetails( - $this->Addon->getListAddons($pop_addon_ids, $valid_status, 'Addon.weeklydownloads DESC') - ); - - $this->publish('popular_addons', $pop_addons); - - $new_addon_ids = $this->Addon->getAddonsFromCategory( - STATUS_PUBLIC, $app_listedtypes[APP_ID], $category, 'newest', - 'DESC', $list_num, 1, '', false - ); - $new_addons = $this->_buildMinimalAddonDetails( - $this->Addon->getListAddons($new_addon_ids, $valid_status, 'Addon.created DESC') - ); - - $this->publish('new_addons', $new_addons); - - $upd_addon_ids = $this->Addon->getAddonsFromCategory( - STATUS_PUBLIC, $app_listedtypes[APP_ID], $category, - 'rated', 'DESC', $list_num, 1, '', false - ); - $upd_addons = $this->_buildMinimalAddonDetails( - $this->Addon->getListAddons($upd_addon_ids, $valid_status, 'Addon.bayesianrating DESC') - ); - - $this->publish('updated_addons', $upd_addons); - + + // fetch another 3 random add-ons + $_feat_ids = $this->AddonTag->getRandomAddons($category, false, 3, null, $addontype=='all' ? null : $addontype); + if (!empty($_feat_ids)) { + $randomAddons = $this->Addon->getListAddons($_feat_ids, $valid_status, + 'FIELD(Addon.id,'.implode(',', $_feat_ids).')', false); + } else { + $randomAddons = false; + } + + $this->publish('featureAddons', $featureAddons); + $this->publish('randomAddons', $randomAddons); + unset($featureAddons); + unset($randomAddons); + // fetch all platforms $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); - + + // set data available to view + $this->publish('type_id', $addontype); + $this->publish('cat_id', $category); + $this->publish('this_tag', $this_tag); $this->set('content_wide', false); // display features next to each other $this->publish('collapse_categories', false); - + // set layout details $this->publish('bigHeader', true); - $this->publish('bigHeaderText', - sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); - - $this->pageTitle = $this_tag['Translation']['name']['string']. " :: " . - sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - $this->publish('rssAdd', array( - "/browse/type:{$addontype}/cat:{$category}/format:rss?sort=updated" - )); + $this->publish('bigHeaderText', sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); + + $this->pageTitle = $this_tag['Translation']['name']['string']. " :: " . sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); + $this->publish('rssAdd', array("/browse/type:{$addontype}/cat:{$category}/format:rss?sort=updated")); $this->render('category_landing'); } @@ -904,16 +650,21 @@ class AddonsController extends AppController * Add-on listing page for a specific category * * Relevant URL format: /browse/type:1/cat:15?sort=name - * + * * @access private */ function _browseAddonsInCategory() { global $valid_status, $app_listedtypes; - + $addontype = $this->namedArgs['type']; $category = $this->namedArgs['cat']; $format = $this->setLayoutForFormat(); - + + // we are sugar + if (true) { + $this_tag = $category == 'all' ? array() : $this->Tag->findById($category); + $addontype = $addontype == 'all' ? $app_listedtypes[APP_SUGAR] : $addontype; + } else // type:1 && cat:all shows a global add-ons list (not extensions only) if ($addontype == ADDON_EXTENSION && $category == 'all') { $this_tag = array(); @@ -922,7 +673,7 @@ class AddonsController extends AppController $this->Tag->unbindFully(); $this_tag = $this->Tag->findById($category); } - + // determine list sort order if (isset($this->params['url']['sort'])) $sort_by = $this->params['url']['sort']; @@ -947,7 +698,7 @@ class AddonsController extends AppController $sort_dir = 'asc'; break; } - + // show experimental add-ons? if (isset($this->params['url']['exp'])) { /* experimental add-ons requested */ @@ -970,7 +721,7 @@ class AddonsController extends AppController } $this->set('show_exp', $show_exp); $displaystatuses = ($show_exp ? $valid_status : array(STATUS_PUBLIC)); - + if ($format != 'rss') { // initialize pagination component $this->Pagination->total = $this->Addon->countAddonsInCategory( @@ -984,7 +735,7 @@ class AddonsController extends AppController $_limit = 20; $_page = 1; } - + // get enough addons for one page. $addons = $this->Addon->getAddonsByCategory(null, $displaystatuses, $addontype, $category, $sort_by, $sort_dir, $_limit, $_page, '', true); @@ -993,14 +744,14 @@ class AddonsController extends AppController return; } $this->publish('addons', $addons); - + // get platforms (if we are not in RSS mode) if ($format != 'rss') { $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); } - + // get other categories list (or all, if this is a complete list) $_tags = $this->Tag->query("SELECT DISTINCT t.id FROM tags AS t " ."INNER JOIN addons_tags AS at ON (t.id = at.tag_id) " @@ -1018,14 +769,15 @@ class AddonsController extends AppController foreach($tag_list as $_tag) $_tag_names[] = $_tag['Translation']['name']['string']; array_multisort($_tag_names, SORT_ASC, $tag_list); } - + // set data available to view $this->publish('this_tag', $this_tag); $this->set('type', $addontype); $this->publish('tagList', $tag_list); - + // set layout details and render view if ($category == 'all') { + // @partial translation fallback, 5/13/08 switch ($sort_by) { case 'popular': $_title = ___('browse_addons_popular'); break; case 'updated': $_title = ___('browse_addons_updated'); break; @@ -1040,15 +792,15 @@ class AddonsController extends AppController if ($format != 'rss') { $this->pageTitle = $_title . " :: " . sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); $this->publish('subpagetitle', $_title); - + // preserve GET variables in RSS feed URLs $_rss_get = array(); foreach($_GET as $_getkey => $_getitem) if ($_getkey != 'url') $_rss_get[] = urlencode($_getkey).'='.urlencode($_getitem); $this->publish('rssAdd', array("/browse/type:{$this->namedArgs['type']}/cat:{$category}/format:rss?".implode('&', $_rss_get))); - + $this->set('collapse_categories', true); - + $this->render('browse'); } else { // RSS feed @@ -1067,9 +819,9 @@ class AddonsController extends AppController global $hybrid_categories, $valid_status; $valid_status = array(STATUS_PUBLIC); - + $format = (isset($this->namedArgs['format']) ? $this->namedArgs['format'] : 'html'); - + // fetch the category belonging to this hybrid page $category = array_search(ADDON_SEARCH, $hybrid_categories[APP_ID]); if ($category) { @@ -1079,20 +831,20 @@ class AddonsController extends AppController $this_tag = null; } $this->publish('this_tag', $this_tag); - + // fetch a list of all subcategories $subcats = $this->Amo->getTags(APP_ID, ADDON_SEARCH); $this->publish('subcats', $subcats); - + // make subcategory ID list to grab recommendations from $subcat_ids = array(); foreach ($subcats as $subcat) $subcat_ids[] = $subcat['Tag']['id']; // add hybrid category for possible other recommendations $subcat_ids[] = $this_tag['Tag']['id']; - - // fetch up to 4 recommended add-ons - $_feat_ids = $this->AddonTag->getRandomAddons($subcat_ids, true, 4); + + // fetch up to 2 recommended add-ons + $_feat_ids = $this->AddonTag->getRandomAddons($subcat_ids, true, 2); if (!empty($_feat_ids)) { $featureAddons = $this->Addon->getListAddons($_feat_ids, $valid_status, null, true); @@ -1100,12 +852,22 @@ class AddonsController extends AppController $featureAddons = array(); } $this->publish('featureAddons', $featureAddons); - + + // fetch 2 random ones. + $_feat_ids = $this->AddonTag->getRandomAddons($subcat_ids, false, 2); + if (!empty($_feat_ids)) { + $randomAddons = $this->Addon->getListAddons($_feat_ids, $valid_status, + null, true); + } else { + $randomAddons = false; + } + $this->publish('randomAddons', $randomAddons); + // fetch all platforms $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); - + if ($format != 'rss') { $this->set('content_wide', true); // display 2 features next to each other $this->set('collapse_categories', true); @@ -1115,7 +877,7 @@ class AddonsController extends AppController $this->publish('bigHeaderText', sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); $this->layout='mozilla'; //$this->publish('rssAdd', array("/browse/type:".ADDON_SEARCH."/format:rss")); - + $this->render('searchengines'); } else { // RSS feed @@ -1135,15 +897,15 @@ class AddonsController extends AppController $this->render('plugins'); return; } - + /** * dictionaries / language tools landing page */ function _dictionaries() { global $valid_status, $native_languages; - + $format = (isset($this->namedArgs['format']) ? $this->namedArgs['format'] : 'html'); - + // get list of target locales $conditions = array( 'Addon.addontype_id' => array(ADDON_DICT, ADDON_LPAPP), @@ -1155,7 +917,7 @@ class AddonsController extends AppController $target_locales = array(); foreach ($target_locales_raw as $tloc) $target_locales[] = $tloc[0]['target_locale']; unset($target_locales_raw); - + // get list of dictionaries and language packs for each target locale $dicts = array(); foreach($target_locales as $tloc) { @@ -1164,7 +926,7 @@ class AddonsController extends AppController ADDON_DICT => array(), ADDON_LPAPP => array() ); - + // get addons for this target locale $conditions = array( 'LOWER(Addon.target_locale)' => $tloc, @@ -1174,7 +936,7 @@ class AddonsController extends AppController ); $this->Addon->unbindfully(); $dicts_raw = $this->Addon->findAll($conditions, null, null, null, null, 2); - + $_dict_ids = array(); foreach ($dicts_raw as $_dict) $_dict_ids[] = $_dict['Addon']['id']; @@ -1183,24 +945,24 @@ class AddonsController extends AppController $tloc_dicts = $this->Addon->getListAddons($_dict_ids, array(STATUS_PUBLIC), null, true); foreach ($tloc_dicts as $dict) { // add add-ons to results array if (empty($dict['File'])) continue; - + // purge add-ons incompatible with this app $compat = $this->Version->getCompatibleApps($dict['Version'][0]['id']); $thisapp_compat = false; foreach ($compat as $compat_app) $thisapp_compat = $thisapp_compat || ($compat_app['Application']['application_id'] == APP_ID); if (!$thisapp_compat) continue; - + $dicts[$tloc][$dict['Addon']['addontype_id']][] = $dict; } unset($dict, $compat, $thisapp_compat, $compat_app); - + /* determine the effective display name for this target locale */ $locale_parts = explode('-', strtolower(str_replace('_', '-', $tloc))); // normalize region part if (isset($locale_parts[1])) $locale_parts[1] = strtoupper($locale_parts[1]); $normalized_locale = implode('-', $locale_parts); - + $displayname = false; while (true) { $locale = strtolower(implode('-', $locale_parts)); @@ -1241,12 +1003,12 @@ class AddonsController extends AppController // sort dictionary list by effective display name uasort($dicts, create_function('$a,$b', 'return strcasecmp($a["displayname"],$b["displayname"]);')); $this->publish('dicts', $dicts); - + // fetch all platforms $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); - + // set layout details $this->pageTitle = _('langtools_header_dicts_and_langpacks') .' :: ' . sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); @@ -1254,7 +1016,7 @@ class AddonsController extends AppController $this->publish('bigHeaderText', sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); $this->layout = 'mozilla'; $this->set('collapse_categories', true); - + $this->render('dictionaries'); } @@ -1265,20 +1027,20 @@ class AddonsController extends AppController global $valid_status; $valid_status = array(STATUS_PUBLIC); - + $format = (isset($this->namedArgs['format']) ? $this->namedArgs['format'] : 'html'); - + // fetch a list of all subcategories $subcats = $this->Amo->getTags(APP_ID, ADDON_THEME); $this->publish('subcats', $subcats); - + // make subcategory ID list to grab recommendations from $subcat_ids = array(); foreach ($subcats as $subcat) $subcat_ids[] = $subcat['Tag']['id']; - + // fetch up to 2 recommended add-ons - $_feat_ids = $this->AddonTag->getRandomAddons($subcat_ids, true, 4); + $_feat_ids = $this->AddonTag->getRandomAddons($subcat_ids, true, 2); if (!empty($_feat_ids)) { $featureAddons = $this->Addon->getListAddons($_feat_ids, $valid_status, null, true); @@ -1286,12 +1048,22 @@ class AddonsController extends AppController $featureAddons = array(); } $this->publish('featureAddons', $featureAddons); - + + // fetch 2 random ones. + $_feat_ids = $this->AddonTag->getRandomAddons($subcat_ids, false, 2); + if (!empty($_feat_ids)) { + $randomAddons = $this->Addon->getListAddons($_feat_ids, $valid_status, + null, true); + } else { + $randomAddons = array(); + } + $this->publish('randomAddons', $randomAddons); + // fetch all platforms $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); - + // set layout details $this->set('content_wide', true); // display 2 features next to each other $this->set('collapse_categories', true); @@ -1299,7 +1071,7 @@ class AddonsController extends AppController $this->publish('bigHeader', true); $this->publish('bigHeaderText', sprintf(_('addons_home_header_details'), APP_PRETTYNAME)); $this->layout = 'mozilla'; - + $this->render('themes_landing'); } @@ -1313,7 +1085,7 @@ class AddonsController extends AppController //override sandbox, recommended page is only public $this->publish('addonStatus', array(STATUS_PUBLIC)); $this->status = array(STATUS_PUBLIC); - + $criteria = "Feature.start < NOW() AND Feature.end > NOW() AND Feature.application_id ='" . APP_ID . "' AND " ."(Feature.locale = '" . LANG . "' or Feature.locale IS NULL)"; if (isset($this->namedArgs['format']) && $this->namedArgs['format'] == 'rss') { @@ -1323,14 +1095,14 @@ class AddonsController extends AppController $isrss = false; $order = "RAND()"; } - + $_addon_ids = array(); - + if(isset($this->namedArgs['cat'])) { $category = $this->namedArgs['cat']; - $this->Amo->clean($category); + $this->Amo->clean($category); $criteria = "feature > 0 AND tag_id='".$category."'"; $featAddons = $this->AddonTag->findAll($criteria); @@ -1338,23 +1110,21 @@ class AddonsController extends AppController $_addon_ids[] = $_addon['AddonTag']['addon_id']; } else { - $featAddons = $this->Feature->findAll($criteria); + $featAddons = $this->Feature->findAll($criteria); foreach ($featAddons as $_addon) $_addon_ids[] = $_addon['Addon']['id']; } - if (!empty($_addon_ids)) { + if (!empty($_addon_ids)) $featAddons = $this->Addon->getListAddons($_addon_ids, $this->status, $order, true); - } else { + else $featAddons = array(); - } - - + if (!$isrss) { // get platforms (if we are not in RSS mode) $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('platforms', $platforms); - + $this->layout='mozilla'; $this->pageTitle = _('addons_recommended_pagetitle').' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); $this->publish('addons', $featAddons); @@ -1368,7 +1138,7 @@ class AddonsController extends AppController $this->render('rss/addons', 'rss'); } } - + /** * page to display eula prior to installation */ @@ -1376,14 +1146,13 @@ class AddonsController extends AppController $this->Amo->clean($lightbox); $this->Amo->clean($addon_id); $this->Amo->clean($file_id); - - $this->layout='amo2009'; - + + $this->layout='mozilla'; if (!$addon_id || !is_numeric($addon_id)) { $this->flash(sprintf(_('error_missing_argument'), 'addon_id'), '/', 3); return; } - + $this->Addon->unbindFully(); $this->Addon->bindModel( array( @@ -1403,31 +1172,22 @@ class AddonsController extends AppController ) ) ); - + $this_addon = $this->Addon->findById($addon_id); if (empty($this_addon)) { $this->flash(_('error_addon_notfound'), '/', 3); return; } - + if (isset($file_id)) { $this->File->unbindFully(); $this_file = $this->File->findById($file_id); $this_addon['Version'] = $this->Version->findAllById($this_file['File']['version_id'], null, null, 0); - - // is this the latest public version? - if ($this_addon['Addon']['status'] == STATUS_PUBLIC) { - $latest_version_id = $this->Version->getVersionByAddonId($addon_id, $this_addon['Addon']['status']); - $this->publish('is_latest', ($latest_version_id === $this_addon['Version'][0]['Version']['id']), false); - } else { - $this->publish('is_latest', false, false); - } - $this->Platform->unbindFully(); $platforms = $this->Platform->findAllById($this_file['File']['platform_id']); $this->publish('platforms', $platforms); } - else { + else { $this->publish('policy', 1); } // get the tags that are related to the addon, so that they have translation data @@ -1437,12 +1197,13 @@ class AddonsController extends AppController } $related_tags = $this->Tag->findAll(array('Tag.id' => $_related_tag_ids, 'Tag.application_id' => APP_ID)); unset($_related_tag_ids); - + $this->publish('relatedTags', $related_tags); $this->publish('addon', $this_addon); + $this->publish('addonIconPath', $this->Image->getAddonIconURL($addon_id), false); $this->pageTitle = sprintf(_('addons_display_pagetitle'), $this_addon['Translation']['name']['string']). ' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - + } @@ -1452,16 +1213,16 @@ class AddonsController extends AppController function previews($id) { $this->Amo->clean($id); $this->layout = 'mozilla'; - + if (!$id || !is_numeric($id)) { $this->flash(sprintf(_('error_missing_argument'), 'addon_id'), '/', 3); return; } - + $addon_data = $this->Addon->find(array( 'Addon.id' => $id, 'Addon.inactive' => '0', - 'Addon.status' => array(STATUS_PUBLIC, STATUS_SANDBOX, STATUS_NOMINATED)), + 'Addon.status' => array(STATUS_PUBLIC, STATUS_SANDBOX, STATUS_NOMINATED)), null , null , 1); if (empty($addon_data)) { $this->flash(_('error_addon_notfound'), '/', 3); @@ -1491,20 +1252,14 @@ class AddonsController extends AppController */ function versions($id) { global $valid_status; - + $this->Amo->clean($id); // unbind addon from all references but its authors $bindusers = array('hasAndBelongsToMany' => array('User' => $this->Addon->hasAndBelongsToMany['User'])); $this->Addon->unbindFully(); $this->Addon->bindModel($bindusers); - $addon = $this->Addon->find(array('Addon.id'=>$id, - 'Addon.status'=>$valid_status, 'Addon.inactive'=>0), null, null, - null, null, 0); - if (empty($addon)) { - $this->flash(_('error_addon_notfound'), '/', 3); - return; - } + $addon = $this->Addon->findById($id, null, null, null, null, 0); // show all valid (even experimental) statuses on versions page $version_list = $this->Version->getVersionIdsByAddonId($id, $valid_status); @@ -1512,39 +1267,36 @@ class AddonsController extends AppController $version_ids = array(); $comp_apps_by_id = array(); foreach ($version_list as $single_id) { - $cur_id = $single_id['Version']['id']; + $cur_id = $single_id['Version']['id']; $version_ids[] = $cur_id; $compat_apps = $this->Version->getCompatibleApps($cur_id); $comp_apps_by_id[$cur_id] = array_slice($compat_apps, 0, 1); - } - - if (!empty($version_ids)) { + } + + if (!empty($version_ids)) { $versions = $this->Version->findAllById($version_ids, null, "Version.created DESC", null, null, 1); - - for($i =0 ; $i < count($versions); $i++) { - $versions[$i]['Compatibility'] = $comp_apps_by_id[$versions[$i]['Version']['id'] ]; - } - } + + for($i =0 ; $i < count($versions); $i++) { + $versions[$i]['Compatibility'] = $comp_apps_by_id[$versions[$i]['Version']['id'] ]; + } + } else $versions = array(); - + $this->Platform->unbindFully(); $platforms = $this->Platform->findAll(); $this->publish('addon', $addon); $this->publish('versions', $versions); $this->publish('platforms', $platforms); - + $_title = sprintf(_('addons_versions_pagetitle'), $addon['Translation']['name']['string']); if (!isset($this->namedArgs['format']) || $this->namedArgs['format'] != 'rss') { $this->publish('addonIconPath', $this->Image->getAddonIconURL($id)); $this->pageTitle = $_title. ' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); $this->publish('subpagetitle', $_title); $this->publish('rssAdd', array("/addons/versions/{$id}/format:rss")); - - $this->publish('breadcrumbs', array( - sprintf(___('addons_home_pagetitle'), APP_PRETTYNAME) => '/', - $addon['Translation']['name']['string'] => "/addon/{$id}" - )); + + $this->layout = 'mozilla'; $this->render(); } else { $this->publish('rss_title', $_title); @@ -1559,19 +1311,19 @@ class AddonsController extends AppController function rss($type='') { $this->Amo->clean($type); $type = strtolower($type); - + switch($type) { case 'newest': $this->redirect('/browse/type:1/cat:all/format:rss?sort=newest'); return; break; - + default: $this->redirect('/'); return; } } - + } ?> diff --git a/site/app/controllers/admin_controller.php b/site/app/controllers/admin_controller.php index d9ae3ec..7495e90 100644 --- a/site/app/controllers/admin_controller.php +++ b/site/app/controllers/admin_controller.php @@ -39,7 +39,7 @@ class AdminController extends AppController { var $name = 'Admin'; - var $uses = array('Addon', 'Addontype', 'Application', 'Approval', 'Appversion', 'Cannedresponse', 'Collection', 'CollectionPromo', 'Eventlog', 'Feature', 'File', 'Group', 'Platform', 'Tag', 'Translation', 'User', 'Version', 'Memcaching'); + var $uses = array('Addon', 'Addontype', 'Application', 'Approval', 'Appversion', 'Cannedresponse', 'Eventlog', 'Feature', 'File', 'Group', 'Platform', 'Tag', 'Translation', 'User', 'Version', 'Memcaching'); var $components = array('Amo', 'Audit', 'Developers', 'Error', 'Versioncompare'); var $helpers = array('Html', 'Javascript'); //These defer to their own access checks @@ -67,11 +67,12 @@ class AdminController extends AppController $this->set('jsAdd', array('developers', 'jquery-compressed.js', - 'jquery.autocomplete.pack.js')); + 'jquery.autocomplete.js')); $this->set('suppressJQuery', 1); $this->breadcrumbs = array('Admin Control Panel' => '/admin/index'); $this->set('breadcrumbs', $this->breadcrumbs); + $this->set('suppressJQuery', 1); $this->set('subpagetitle', 'Admin Control Panel'); @@ -113,7 +114,7 @@ class AdminController extends AppController } if (!defined('USERAPP')) { - define('USERAPP', 1); // Firefox + define('USERAPP', SITE_APP); // Firefox } $this->Session->write('Features', array('userlang' => USERLANG, 'userapp' => USERAPP)); @@ -151,10 +152,11 @@ class AdminController extends AppController $this->set('last24', $last24); //Counts - $count['extensions'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_EXTENSION); - $count['themes'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_THEME); - $count['dictionaries'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_DICT); - $count['searchengines'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_SEARCH); + $count['activities'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_ACTIVITY); + //$count['extensions'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_EXTENSION); + //$count['themes'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_THEME); + //$count['dictionaries'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_DICT); + //$count['searchengines'] = $this->Addon->query("SELECT COUNT(*) FROM addons WHERE addontype_id=".ADDON_SEARCH); $now = time(); $count['activeSessions'] = $this->Addon->query("SELECT COUNT(*) FROM cake_sessions WHERE expires > {$now}"); $this->set('count', $count); @@ -522,77 +524,6 @@ class AdminController extends AppController $this->set('page', 'applications'); $this->render('applications_create'); } - - /** - * Collections Manager - */ - function collections($action='') { - switch($action) { - case 'promobox': - $this->_collectionsPromoBox(); - break; - default: - $this->set('page', 'collections'); - $this->render('collections'); - break; - } - } - - function _collectionsPromoBox() { - - if (!empty($_POST)) { - switch ($_POST['action']) { - case 'add': - global $valid_languages; - // It's easiest just to split out the stuff we need here and pass it into a custom function. - if (preg_match('/\[(\d+)\]/', $this->data['Collection']['name'], $matches)) { - $_collection_id = $matches[1]; - } else { - $this->Error->addError('Collection ID must be specified in brackets.'); - } - - if (in_array($_POST['locale'], array_keys($valid_languages))) { - $_locale = $_POST['locale']; - } else if ($_POST['locale'] == 'all') { - $_locale = ''; - } else { - $this->Error->addError('Invalid locale specified.'); - } - - if (in_array($_POST['titletagline'], array_keys($this->CollectionPromo->titles_and_taglines))) { - $_titletagline = $_POST['titletagline']; - } else { - $this->Error->addError('Invalid Title/TagLine specified.'); - } - - if ($this->Error->noErrors()) { - $this->CollectionPromo->promoteCollection($_collection_id, $_titletagline, $_locale); - $this->data = array(); // reset so it doesn't prefill the box again - $this->publish('changeSuccess', true); - } - break; - case 'delete': - list($_collection_id, $_titletagline, $_locale) = explode('--',$_POST['target']); - $_locale = ($_locale == 'all') ? '' : $_locale; - - if ($this->CollectionPromo->demoteCollection($_collection_id, $_titletagline, $_locale) === false) { - $this->Error->addError('Failed to demote collection.'); - } else { - $this->publish('changeSuccess', true); - } - break; - } - } - - $this->set('titles_and_taglines', $this->CollectionPromo->titles_and_taglines); - $this->publish('promoted_collections', $this->CollectionPromo->findAll()); - $this->set('locales', LANGUAGE_CONFIG::getAllValidLanguages('english',true)); - - $this->set('errors', $this->Error->errors); - $this->set('page', 'collections'); - $this->set('subpage', 'promobox'); - $this->render('collections_promobox'); - } /** * Category Manager @@ -607,6 +538,10 @@ class AdminController extends AppController $this->set('breadcrumbs', $this->breadcrumbs); $applications = array('All'); + + // we are sugar + $applications = array(); + $_applications = $this->Amo->getApplicationName(); if (!empty($_applications)) { foreach ($_applications as $app_id => $app_name) { @@ -1541,7 +1476,7 @@ class AdminController extends AppController if (!empty($this->data['User']['password'])) { $session = $this->Session->read('User'); - if ($this->User->checkPassword($session, $this->data['User']['password'])) { + if (md5($this->data['User']['password']) == $session['password']) { $this->set('config', $config); $this->set('rand', $sessionConfig['rand']); @@ -1629,17 +1564,16 @@ class AdminController extends AppController if (!empty($flagged)) { foreach ($flagged as $k => $addon) { $version = $this->Version->findByAddon_id($addon['Addon']['id'], - array('Version.id', 'Version.addon_id', 'Version.version'), - 'Version.created DESC'); - if (!$version) $version = array(); + array('Version.id', 'Version.addon_id', + 'Version.version' + ), 'Version.created DESC'); $flagged[$k] = array_merge_recursive($flagged[$k], $version); $history = $this->Approval->find( - "Approval.addon_id={$addon['Addon']['id']} AND - ((Approval.reviewtype='nominated' AND Approval.action=".STATUS_NOMINATED.") OR - (Approval.reviewtype='pending' AND Approval.action=".STATUS_PENDING."))", - null, 'Approval.created DESC'); - if (!$history) $history = array(); + "Approval.addon_id={$addon['Addon']['id']} AND + ((Approval.reviewtype='nominated' AND Approval.action=".STATUS_NOMINATED.") OR + (Approval.reviewtype='pending' AND Approval.action=".STATUS_PENDING."))", + null, 'Approval.created DESC'); $flagged[$k] = array_merge_recursive($flagged[$k], $history); } } @@ -1657,8 +1591,7 @@ class AdminController extends AppController function users($user_id = 0, $type = 'edit') { $this->breadcrumbs['User Manager'] = '/admin/users'; $this->set('breadcrumbs', $this->breadcrumbs); - $this->set('suppressJQuery', 0); - + if (!empty($user_id)) { $this->Amo->clean($user_id); $this->User->id = $user_id; @@ -1704,10 +1637,6 @@ class AdminController extends AppController case 'edit': default: $this->User->save($this->data['User']); - - // save author "about me" - list($localizedFields, $unlocalizedFields) = $this->User->splitLocalizedFields($this->data['User']); - $this->User->saveTranslations($user_id, $this->params['form']['data']['User'], $localizedFields); //Log admin action $this->Eventlog->log($this, 'admin', 'user_edit', null, $user_id); @@ -1718,9 +1647,6 @@ class AdminController extends AppController } else { $user = $this->User->read(); - // grab translated fields - $translations = $this->User->getAllTranslations($user_id); - $this->set('translations', $translations); } } elseif (!empty($_GET['q'])) { @@ -1731,9 +1657,6 @@ class AdminController extends AppController $this->flash('E-mail not found.', '/admin/users'); return; } - // grab translated fields - $translations = $this->User->getAllTranslations($user['User']['id']); - $this->set('translations', $translations); } $this->set('page', 'users'); @@ -1803,38 +1726,5 @@ class AdminController extends AppController $this->set('results', $results); $this->render('userlookup', 'ajax'); } - - /** - * AJAX Collection lookup. Matches either name or nickname - */ - function collectionLookup() { - if (!$this->SimpleAcl->actionAllowed('Admin', '%', $this->Session->read('User')) || - !$this->SimpleAcl->actionAllowed('Editor', '*', $this->Session->read('User')) ) { - $this->Amo->accessDenied(); - } - global $app_shortnames; - - $text = $_REQUEST['q']; - $this->Amo->clean($text); - $results = array(); - - $_query = "SELECT - collections.id, translations.localized_string, collections.nickname, collections.application_id - FROM collections LEFT JOIN translations ON collections.name=translations.id - WHERE (translations.locale='".LANG."' - AND translations.localized_string LIKE '%{$text}%') - OR collections.nickname LIKE '%{$text}%' - ORDER BY translations.localized_string"; - - if ($collections = $this->Collection->query($_query)) { - foreach ($collections as $collection) { - $_application = array_search($collection['collections']['application_id'], $app_shortnames); - $results[] = "{$collection['translations']['localized_string']}|ID: {$collection['collections']['id']}|App: {$_application}|nickname: {$collection['collections']['nickname']};"; - } - } - - $this->set('results', $results); - $this->render('userlookup', 'ajax'); - } } ?> diff --git a/site/app/controllers/api_controller.php b/site/app/controllers/api_controller.php index 33a85ce..1e41f27 100644 --- a/site/app/controllers/api_controller.php +++ b/site/app/controllers/api_controller.php @@ -52,7 +52,7 @@ class ApiController extends AppController // cribbed from addonscontroller // some of this is excessive but will likely be needed as // development continues - var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox', 'checkAdvancedSearch'); + var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox'); var $uses = array('Addon', 'AddonCollection', 'Addontype', 'Application', 'Collection', 'File', 'Platform', 'Tag', 'Translation', /*'Review',*/ 'UpdateCount', 'Version'); var $components = array('Amo', 'Image', 'Pagination', 'Search', 'Session', 'Versioncompare'); var $helpers = array('Html', 'Link', 'Time', 'Localization', 'Ajax', 'Number', 'Pagination'); diff --git a/site/app/controllers/collections_controller.php b/site/app/controllers/collections_controller.php index e9b7bfd..da80d85 100644 --- a/site/app/controllers/collections_controller.php +++ b/site/app/controllers/collections_controller.php @@ -39,705 +39,75 @@ class CollectionsController extends AppController { var $name = 'Collections'; - var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox', 'checkAdvancedSearch'); - var $uses = array('Addon', 'AddonCollection', 'Application', 'Collection', 'File', - 'Platform', 'Preview', 'Translation', 'Version'); - var $components = array('Amo', 'CollectionsListing', 'Developers', 'Error', 'Helper', 'Image', 'Pagination', 'Session'); - var $actionHelpers = array('Html'); - var $helpers = array('Html', 'Link', 'Listing', 'Time', 'Localization', 'Pagination', 'Number', 'Form'); + var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox'); + var $uses = array('Addon', 'AddonTag', 'Addontype', 'Application', + 'Feature', 'File', 'Platform', 'Preview', 'Tag', 'Translation', + 'Review', 'Version', 'Collection'); + var $components = array('Amo', 'Image', 'Pagination', 'Session', 'Userfunc', 'Search'); + var $helpers = array('Html', 'Link', 'Time', 'Localization', 'Ajax', 'Number', 'Pagination'); var $exceptionCSRF = array("/collections/install"); var $namedArgs = true; var $securityLevel = 'low'; function beforeFilter() { - $this->layout='mozilla'; - $this->publish('collapse_categories', true); - $this->publish('collectionSearch', true); - $this->pageTitle = 'Collections' . " :: " . sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - $this->publish('jsAdd', array('jquery.autocomplete.pack.js'), false); // Disable ACLs because this controller is entirely public. $this->SimpleAuth->enabled = false; $this->SimpleAcl->enabled = false; - - // disable query caching so devcp changes are visible immediately - if ($this->Session->check('User')) { - foreach ($this->uses as $_model) { - $this->$_model->caching = false; - } - } - - $this->publish('jsAdd', array('amo2009/collections', 'jquery-ui/jqModal.js')); + $this->layout='mozilla'; + $this->publish('collapse_categories', true); + $this->pageTitle = 'Collections' . " :: " . sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); } - + function index() { - /* TODO: preserve get params */ - $this->redirect('/collections/editors_picks'); + } - - function _listing($collections, $pagination_options=array()) { - $ids = array(); - foreach ($collections as $c) $ids[] = $c['Collection']['id']; - - $collections = $this->CollectionsListing->fetchPage($ids, $pagination_options); - list($sort_opts, $sortby) = $this->CollectionsListing->sorting(); - - $this->publish('sort_opts', $sort_opts); - $this->publish('sortby', $sortby); - $this->publish('collections', $collections); - $this->set('tabs', $this->_collectionTabs()); - - // if a collection was just deleted, show success message - $this->publish('collection_deleted', $this->Session->delete('collection_deleted'), false); - - $this->publish('breadcrumbs', array( - sprintf(___('addons_home_pagetitle'), APP_PRETTYNAME) => '/', - )); - - $this->render('listing'); - } - - function _collectionTabs() { - $tabs = array( - array('href' => 'editors_picks', - 'text' => ___('collections_index_li_editors')), - array('href' => 'popular', - 'text' => ___('collections_index_li_popular')), - ); - - if ($this->Session->check('User')) { - $tabs = array_merge($tabs, array( - array('href' => 'mine', - 'text' => ___('collections_index_li_mine')), - array('href' => 'favorites', - 'text' => ___('collections_index_li_favorites')), - )); - } - return $tabs; - } - - - function editors_picks() { - $this->set('selected', 'editors_picks'); - $conditions = array('Collection.collection_type' => Collection::COLLECTION_TYPE_EDITORSPICK, - 'Collection.listed' => 1); - $this->Collection->unbindFully(); - $collections = $this->Collection->findAll($conditions, 'Collection.id'); - $this->_listing($collections); - } - - function popular() { - $this->set('selected', 'popular'); - $this->Collection->unbindFully(); - $collections = $this->Collection->findAll(array('Collection.listed' => 1), - 'Collection.id'); - $pagination = array('sortBy' => 'subscribers DESC'); - $this->_listing($collections, $pagination); - } - - function mine() { - $this->Amo->checkLoggedIn(); - - $this->set('selected', 'mine'); - $this->set('filler', sprintf(___('collections_index_filler_mine'), - $this->Html->url('/collections/add'))); - - $user = $this->Session->read('User'); - $collections = $this->Collection->execute( - "SELECT Collection.id - FROM collections AS Collection JOIN collections_users - ON Collection.id = collections_users.collection_id - WHERE collections_users.user_id = " . $user['id']); - $this->_listing($collections); - } - - function favorites() { - $this->Amo->checkLoggedIn(); - $this->set('selected', 'favorites'); - $this->set('filler', sprintf(___('collections_index_filler_favorites'), - $this->Html->url('/pages/collector'))); - - $user = $this->Session->read('User'); - $collections = $this->Collection->execute( - "SELECT Collection.id - FROM collections AS Collection JOIN collection_subscriptions - ON Collection.id = collection_subscriptions.collection_id - WHERE collection_subscriptions.user_id = " . $user['id']); - $this->_listing($collections); - } - - /** - * Creates a collection if POSTed to, otherwise shows a collection creation form - */ - function add() { - $this->Amo->checkLoggedIn(); // must be logged in - - // view setup - $this->layout = 'amo2009'; // TODO: remove this when the entire controller is amo2009-based - $this->set('bodyclass', 'inverse collections-page'); - $this->publish('jsAdd', array('jquery.autocomplete.pack.js')); - $this->publish('breadcrumbs', array( - sprintf(___('addons_home_pagetitle'), APP_PRETTYNAME) => '/', - ___('collections_breadcrumb') => '/collections' - )); - - $initial_addons = array(); - if (!empty($this->params['url']['addons'])) { - $_init_ids = explode(',', $this->params['url']['addons']); - foreach ($_init_ids as &$_init_id) { - if (!is_numeric($_init_id)) continue; - $_addon = $this->Addon->getAddon($_init_id); - if (empty($_addon)) continue; - $initial_addons[] = array( - 'id' => $_addon['Addon']['id'], - 'name' => $_addon['Translation']['name']['string'], - 'preview' => $this->Image->getAddonIconURL($_init_id) - ); - } - } - $this->publish('initial_addons', $initial_addons); - - if (isset($this->data['Collection'])) { - // clean up whitespace - $this->data['Collection']['name'] = trim($this->data['Collection']['name']); - $this->data['Collection']['description'] = trim($this->data['Collection']['description']); - - $user = $this->Session->read('User'); - $this->data['Collection']['user_id'] = $user['id']; - $this->data['Collection']['application_id'] = APP_ID; // defaults to current app - $this->data['Collection']['defaultlocale'] = LANG; // defaults to current lang - - $data = $this->data['Collection']; - $this->Amo->clean($data); - if ($this->Collection->save($data)) { - $collectionid = $this->Collection->id; // new collection id - $_coll = $this->Collection->findById($collectionid, array('Collection.uuid')); - - $this->Collection->addUser($this->Collection->id, $user['id'], COLLECTION_ROLE_OWNER); - - if (!empty($this->params['form']['addons'])) { - // add-ons preselected - $this->Amo->clean($this->params['form']['addons']); - foreach ($this->params['form']['addons'] as &$addon) { - $this->Collection->addAddonToCollection($collectionid, $user['id'], $addon); - } - } - - $this->Session->write('collection_created', $collectionid); - $this->redirect("/collections/view/{$_coll['Collection']['uuid']}"); - return; - } else { - $this->set('form_errors', true); - } - } - } - - /** - * Non-JS only: Adds an add-on to a collection, then redirects to display page - */ - function addtocollection() { - $this->Amo->checkLoggedIn(); // must be logged in - - if (empty($this->data['addon_id']) || empty($this->data['collection_uuid'])) { - $this->flash(sprintf(_('error_missing_argument'), 'addon_id or collection_id'), '/', 3); - return; - } - - // create new collection if requested - if ($this->data['collection_uuid'] == 'new') { - if (is_array($this->data['addon_id'])) - $addonids = implode(',', $this->data['addon_id']); - else - $addonids = $this->data['addon_id']; - $addonids = urlencode($addonids); - $this->redirect("/collections/add/?addons={$addonids}"); + + function display($id = NULL) { + $this->set('jsAdd', array('jquery-ui/ui.core.min.js', 'jquery-ui/ui.accordion.min.js', 'jquery-ui/jqModal.js')); + + $this->Amo->clean($id); + if (!$id || !is_numeric($id)) { + $this->flash(sprintf(_('error_missing_argument'), 'collection_id'), '/', 3); return; } - - $this->Amo->clean($this->data); - $addon_id = $this->data['addon_id']; - $collection_id = $this->Collection->getIdForUUID($this->data['collection_uuid']); - if (!is_numeric($addon_id) || !$collection_id) { - $this->flash(sprintf(_('error_missing_argument'), 'addon_id or collection_id'), '/', 3); + + $_conditions = array( + 'Collection.id' => $id, + 'Collection.listed' => 1, + ); + + $this->Collection->unbindFully(); + $collection = $this->Collection->find($_conditions, null, null, 1); + + if($collection == null) { + $this->flash(_('collection_not_found'), '/', 3); return; } - $user = $this->Session->read('User'); - $added = $this->Collection->addAddonToCollection($collection_id, $user['id'], $addon_id); - // go to add-on's display page and display success message - $this->Session->write('collection_addon_added', $this->data['collection_uuid']); - $this->redirect("/addon/{$addon_id}"); - return; - } - - function _getSortedAddons($collection_id) { - $sort_options = array( - 'date-added' => ___('collections_detail_sort_date'), - 'name' => ___('collections_detail_sort_name'), - 'popularity' => ___('collections_detail_sort_popularity') - ); - - // Fetch #1. What's in the collection? - $addonIds = $this->Addon->getAddonsFromCollection($collection_id); - - // Set up pagination - $this->Pagination->total = count($addonIds); - $this->Pagination->show = 7; - list($order, $limit, $page) = $this->Pagination->init(); - - // Default sorting is by date added to Collection. - if (isset($_GET['sortby']) && array_key_exists($_GET['sortby'], $sort_options)) { - $sortby = $_GET['sortby']; - } else { - $sortby = 'date-added'; - } - - // Fetch #2. Sort and fetch paged addon ids. - if ($sortby == 'date-added') { - $field = 'addons_collections.added DESC'; - $extra = 'JOIN addons_collections ON Addon.id = addons_collections.addon_id'; - $pagedIds = $this->Addon->sorted($addonIds, $field, $limit, $page, $extra); - } else { - if ($sortby == 'popularity') { - $field = 'weeklydownloads DESC'; - } else if ($sortby == 'name') { - $field = 'name'; - } - $pagedIds = $this->Addon->sorted($addonIds, $field, $limit, $page); - } - - // Fetch #3! Pull useful addon data this time. - $addons = $this->Addon->getAddonList($pagedIds,array( + + $addonIds = $this->Addon->getAddonsFromCollection($id); + + $addons = $this->Addon->getAddonList($addonIds,array( 'all_tags', 'authors', 'compatible_apps', 'files', 'latest_version', 'list_details')); - + foreach($addons as &$addon) { - $a = &$addon['Addon']; - // Get publish details for each add-on - $publishDetails = $this->Addon->getCollectionPublishDetails($a['id'], $collection_id); - $a = array_merge($a, $publishDetails); - } - - return array($addons, $sort_options, $sortby); - } - - function view($uuid = NULL) { - if (!$uuid) { - $this->flash(sprintf(_('error_missing_argument'), 'collection_id'), '/', 3); - return; + $addonId = $addon['Addon']['id']; + $addon['Addon']['dateadded'] = $this->Addon->getDateAddedToCollection($addonId, $id); } - - $id = $this->Collection->getIdForUuidOrNickname($uuid); - if (!$id) { - $this->flash(_('collection_not_found'), '/', 3); - return; - } - $_conditions['Collection.id'] = $id; - - $collection = $this->Collection->find($_conditions, null, null, 1); - - list($addons, $sort_options, $sortby) = $this->_getSortedAddons($collection['Collection']['id']); - + $this->publish('addons', $addons); $this->publish('collection', $collection); - $this->publish('sort_options', $sort_options); - $this->publish('sortby', $sortby); $this->pageTitle = $collection['Translation']['name']['string'] . " :: " . sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - - // User-specific stuff. - if ($this->Session->check('User')) { - $user = $this->Session->read('User'); - $is_subscribed = $this->Collection->isSubscribed($id, $user['id']); - if ($is_subscribed) { - $action = $this->Collection->getUnsubscribeUrl(); - } else { - $action = $this->Collection->getSubscribeUrl(); - } - $this->publish('is_subscribed', $is_subscribed); - $this->publish('subscribe_action', $action); - $this->_getUserRights($user, $id); - } else { - // Use 0 as a dummy user. - $this->_getUserRights(array('id' => 0), $id); - } - - // was the collection just created? show success message - $collection_created = $this->Session->read('collection_created'); - if ($collection_created == $id) $this->Session->delete('collection_created'); - $this->publish('collection_created', ($collection_created == $id)); - - $rss_url = sprintf('/collection/%s?format=rss', $collection['Collection']['uuid']); - $this->publish('rssAdd', array( - array($rss_url, - sprintf(___('collection_detail_rss_title'), - $collection['Translation']['name']['string'])) - )); - + + if (isset($_GET['format']) && $_GET['format'] == 'rss') { - $this->publish('atom_self', $rss_url); $this->publish('rss_title', $collection['Translation']['name']['string']); $this->publish('rss_description', $collection['Translation']['description']['string']); - return $this->render('rss/collection', 'rss'); - } - - $this->publish('breadcrumbs', array( - sprintf(___('addons_home_pagetitle'), APP_PRETTYNAME) => '/', - ___('collections_breadcrumb') => '/collections' - )); - - $this->render('detail'); - } - - /** - * Subscribes user to the collection - */ - function subscribe($ajax = null) { - $this->_subscribe_unsubscribe($ajax); - } - - /** - * Unsubscribe a user from a collection - */ - function unsubscribe($ajax = null) { - $this->_subscribe_unsubscribe($ajax); - } - - /** - * Combined function for subscribing/unsubscribing. Action is determined by - * $this->action. - * @access private - * @param string $ajax undefined or 'ajax' for no-frills rendering - * @return bool render()ed successfully? - */ - function _subscribe_unsubscribe($ajax = null) { - $this->Amo->checkLoggedIn(); // must be logged in - - $this->publish('is_ajax', ($ajax == 'ajax')); - - if (!in_array($this->action, array('subscribe', 'unsubscribe'))) { - $this->flash(___('error_access_denied'), '/', 3); - return; - } - - if (empty($this->params['form']['uuid'])) { // uuid needs to be POSTed - $this->flash(sprintf(_('error_missing_argument'), 'uuid'), '/', 3); - return; - } - $uuid = $this->params['form']['uuid']; - $id = $this->Collection->getIdForUuidOrNickname($uuid); - if (!$id || !is_numeric($id)) { - $this->set('success', false); - return $this->render('subscribe'); - } - - $user = $this->Session->read('User'); - if ($this->action == 'subscribe') { - $result = $this->Collection->subscribe($id, $user['id']); - } else { - $result = $this->Collection->unsubscribe($id, $user['id']); + $this->render('rss/collection', 'rss'); } - $collection = $this->Collection->findById($id, array('id', 'name'), null, -1); - $this->set('success', $result); - - $this->publish('collection', $collection); - - // set up view and render result - $this->publish('breadcrumbs', array( - sprintf(___('addons_home_pagetitle'), APP_PRETTYNAME) => '/', - ___('collections_breadcrumb') => '/collections' - )); - return $this->render('subscribe'); } - - /** - * Edit collection - */ - function edit($uuid = null) { - $this->Amo->checkLoggedIn(); // must be logged in - - // disable query caching so changes are visible immediately - $this->Collection->caching = false; - - if (empty($uuid)) { - $this->flash(sprintf(_('error_missing_argument'), 'collection_id'), '/', 3); - return; - } - $id = $this->Collection->getIdForUuidOrNickname($uuid); - if (!$id) { - $this->flash(_('collection_not_found'), '/', 3); - return; - } - - // access rights - $user = $this->Session->read('User'); - $this->publish('user', $user); - $rights = $this->_getUserRights($user, $id); - if (!($rights['writable'] || $rights['isadmin'])) { - $this->flash(___('error_access_denied'), '/', 3); - return; - } - - if (!empty($this->data)) { - // Delete collection? - if (isset($this->data['action']) && $this->data['action'] == 'delete-coll') { - if (!$rights['atleast_owner']) { - $this->flash(___('error_access_denied'), '/', 3); - return; - } - $this->Collection->delete($id); - $this->Session->write('collection_deleted', true); - $this->redirect("/collections"); - return; - } - - // regular save - $success = $this->_saveCollectionEdit($id, $rights); - - if ($success) { - // grab updated collection nickname/uuid - $updated = $this->Collection->findById($id, array('nickname', 'uuid')); - $this->publish('collection_saved', true, false); - if (empty($updated['Collection']['nickname'])) - $this->publish('collection_url', "/collection/{$updated['Collection']['uuid']}"); - else - $this->publish('collection_url', "/collection/{$updated['Collection']['nickname']}"); - } else { - $this->set('form_errors', true); - } - } - - // get collection data for display (and publish to view) - $this->_getCollectionDataForView($id); - - // view setup - $this->layout = 'amo2009'; // TODO: remove this when the entire controller is amo2009-based - $this->set('bodyclass', 'inverse collections-page'); - $this->publish('jsAdd', array('jquery-ui/ui.core.min', 'jquery-ui/ui.tabs.min', - 'jquery.autocomplete.pack.js')); - $this->publish('breadcrumbs', array( - sprintf(___('addons_home_pagetitle'), APP_PRETTYNAME) => '/', - ___('collections_breadcrumb') => '/collections' - )); - } - - /** - * get collection data for collection edit page (and publish it to view) - * @param int $id collection ID - * @return void - * @access private - */ - function _getCollectionDataForView($id) { - $this->Collection->unbindModel(array('hasAndBelongsToMany'=>array('Addon'))); - $collection = $this->Collection->findById($id); - $this->data['Collection'] = $collection; - - // translations - $translations = $this->Collection->getAllTranslations($id); - $this->publish('translations', $translations); - - // addons - $addons = $this->AddonCollection->getAddonsFromCollection($id); - $this->publish('addons', $addons); - $addons_noscript = array(); - foreach ($addons as &$addon) { - $addons_noscript[$addon['AddonCollection']['addon_id']] = $addon['Addon']['Translation']['name']['string']; - } - $this->publish('addons_noscript', $addons_noscript); - - // collection icon - $this->publish('iconurl', $this->Image->getCollectionIconURL($id), false); - - // prepare applications - global $app_shortnames, $app_prettynames; - $appoptions = array(); - foreach ($app_shortnames as $sn => &$no) { - $appoptions[$no] = $app_prettynames[$sn]; - } - $this->publish('appoptions', $appoptions, false); - - // prepare collection types - $this->publish('collection_types', array( - Collection::COLLECTION_TYPE_NORMAL => ___('collections_type_normal'), - Collection::COLLECTION_TYPE_AUTOPUBLISHER => ___('collections_type_autopublisher'), - Collection::COLLECTION_TYPE_EDITORSPICK => ___('collections_type_editorspick') - ), false); - - // get existing publishers and managers - $publishers = $this->Collection->getUsers($id, array(COLLECTION_ROLE_PUBLISHER)); - $publishers_noscript = $managers_noscript = array(); - foreach ($publishers as &$p) { - $publishers_noscript[$p['User']['id']] = $p['User']['email']; - } - $managers = $this->Collection->getUsers($id, array(COLLECTION_ROLE_ADMIN)); - foreach ($managers as &$m) { - $managers_noscript[$m['User']['id']] = $m['User']['email']; - } - $this->publish('publishers', $publishers); - $this->publish('publishers_noscript', $publishers_noscript); - $this->data['Publishers']['p_onlyme'] = (int)empty($publishers); - $this->publish('managers', $managers); - $this->publish('managers_noscript', $managers_noscript); - $this->data['Managers']['m_onlyme'] = (int)empty($managers); - } - - /** - * save collection data from edit page - * - * @param int $id Collection ID - * @param array $rights user rights array from _getUserRights() - * @return bool successfully saved? - * @access private - */ - function _saveCollectionEdit($id, $rights) { - $disallowed_fields = array('uuid', 'access', 'subscribers', - 'created', 'modified', 'downloads'); - if (!$rights['isadmin']) $allowed_fields[] = 'collection_type'; - $collectiondata = $this->Amo->filterFields($this->data['Collection'], - array(), $disallowed_fields); - $this->Collection->id = $id; - - list($localizedFields, $unlocalizedFields) = $this->Collection->splitLocalizedFields($collectiondata); - // clean up whitespace - foreach ($localizedFields as $field => &$translations) { - foreach ($translations as $lang => &$l_string) { - $l_string = trim($l_string); - $this->params['form']['data']['Collection'][$field][$lang] = $l_string; - } - } - - $valid_translations = $this->Collection->validateTranslations($localizedFields); - if ($valid_translations) { - $this->Amo->clean($localizedFields); - $this->Collection->saveTranslations($id, $this->params['form']['data']['Collection'], $localizedFields); - } - - // handle icon removal/upload - if (!empty($this->data['Icon']['delete'])) { - $unlocalizedFields['icontype'] = ''; - $unlocalizedFields['icondata'] = null; - } elseif (!empty($_FILES['icon']['name'])) { - $iconData = $this->Developers->validateIcon($_FILES['icon']); - if (is_array($iconData)) { - $unlocalizedFields = array_merge($unlocalizedFields, $iconData); - } - } - - // field post-processing - if (!isset($unlocalizedFields['listed'])) $unlocalizedFields['listed'] = 0; - if (!empty($unlocalizedFields['nickname'])) - $unlocalizedFields['nickname'] = preg_replace(INVALID_COLLECTION_NICKNAME_CHARS, - '_', mb_strtolower(trim($unlocalizedFields['nickname']))); - - if ($success = $this->Collection->save($unlocalizedFields)) { - // save noscript data - if ($rights['atleast_manager']) { - // save users - $this->_saveCollectionEditUserData($id, 'Publishers'); - $this->_saveCollectionEditUserData($id, 'Managers'); - } // at least manager - - // add-ons - // remove old add-ons - if (!empty($this->data['Addons']['delete'])) { - foreach ($this->data['Addons']['delete'] as &$_aid) { - $this->AddonCollection->deleteByAddonIdAndCollectionId($_aid, $id, ($rights['atleast_manager'] ? null : $user['id'])); - } - } - // add new add-ons - if (!empty($this->params['form']['q'])) { - $_aids = explode(',', $this->params['form']['q']); - foreach ($_aids as &$_aid) { - $_aid = trim($_aid); - if (!empty($_aid)) { - $_addon = $this->Addon->getAddon($_aid); - if (!empty($_addon) && in_array($_addon['Addon']['status'], $valid_status)) - $this->Collection->addAddonToCollection($id, $user['id'], $_aid); - } - } - } - } - return $success; - } - - /** - * save user data (managers/publishers) for collection edit - * @param int $id collection ID - * @param string $type "Publishers" or "Managers" - * @return void - * @access private - */ - function _saveCollectionEditUserData($id, $type) { - switch ($type) { - case 'Publishers': - $onlyme_fieldid = 'p_onlyme'; - $role = COLLECTION_ROLE_PUBLISHER; - break; - case 'Managers': - $onlyme_fieldid = 'm_onlyme'; - $role = COLLECTION_ROLE_ADMIN; - break; - default: - return; - } - - if (isset($this->data[$type][$onlyme_fieldid]) && $this->data[$type][$onlyme_fieldid]) { - // remove all existing users with this role - $this->Collection->removeAllUsersByRole($id, $role); - } else { - // remove old users - if (!empty($this->data[$type]['delete'])) { - foreach ($this->data[$type]['delete'] as &$_pid) { - $this->Collection->removeUser($id, $_pid); - } - } - // save new users - if (!empty($this->data[$type]['new'])) { - $_emails = explode(',', $this->data[$type]['new']); - foreach ($_emails as &$_em) { - $_em = trim($_em); - if (empty($_em)) continue; - $_uid = $this->User->findByEmail($_em, array('id')); - if (empty($_uid)) continue; - $this->Collection->addUser($id, $_uid['User']['id'], $role); - } - } - // don't publish them back to the view - $this->data[$type]['new'] = ''; - } - } - - /** - * get user rights for a specific collection - * @param array $user array from user model - * @param int $collection_id - * @return array of booleans ('writable', 'isadmin', 'atleast_manager', - * 'atleast_owner', 'role') - * @access private - */ - function _getUserRights($user, $collection_id) { - $writable = $this->Collection->isWritableByUser($collection_id, $user['id']); - $isadmin = $this->SimpleAcl->actionAllowed('Admin', 'EditAnyCollection', $user); - $role = $this->Collection->getUserRole($collection_id, $user['id']); - $atleast_manager = ($isadmin || in_array($role, array(COLLECTION_ROLE_ADMIN, COLLECTION_ROLE_OWNER))); - $atleast_owner = ($isadmin || $role == COLLECTION_ROLE_OWNER); - - $this->publish('writable', $writable, false); - $this->publish('isadmin', $isadmin, false); - $this->publish('atleast_manager', $atleast_manager, false); - $this->publish('atleast_owner', $atleast_owner, false); - $this->publish('role', $role, false); - - return array( - 'writable' => $writable, - 'isadmin' => $isadmin, - 'atleast_manager' => $atleast_manager, - 'atleast_owner' => $atleast_owner, - 'role' => $role - ); - } - - - /*** Fashion Your Firefox ***/ - + /** * Special "interactive collections" page for first-time users */ @@ -747,28 +117,28 @@ class CollectionsController extends AppController $this->redirect('/'); exit(); } - + // XXX: for accessibility (bug 462411), we use a hand-bundled accordion // + core jquery UI file. Once jquery UI is updated to post-1.6.2rc2, // the regular jquery UI accordion should be used (cf. jquery ui bug // 3553, http://ui.jquery.com/bugs/ticket/3553) $this->set('jsAdd', array('jquery-ui/jq-ui-162rc2-accordion-bundle-a11y.min.js', 'jquery-ui/jqModal.js')); $this->set('cssAdd', array('collection-style')); - + $addonIds = $this->Addon->getCategorizedAddonsFromCollection(1); // special collection ID $addons = array(); foreach ($addonIds as $catid => $cataddons) { $addons[$catid] = $this->Addon->getAddonList($cataddons, array('files', 'latest_version', 'list_details')); } $this->publish('addons', $addons); - + // prepare view, then render $this->publish('suppressHeader', true, false); $this->publish('suppressLanguageSelector', true, false); $this->publish('suppressCredits', true, false); $this->pageTitle = 'Fashion Your Firefox'; } - + /** * installation dialog for collections * @param string $method 'html' (with layout) or 'ajax' (without) @@ -778,7 +148,7 @@ class CollectionsController extends AppController if (!empty($_POST['addon'])) { $addons = $this->Addon->getAddonList($_POST['addon'], array( 'compatible_apps', 'files', 'latest_version', 'list_details')); - + // XXX: ugly hack allowing signed add-ons to be installed separately // due to bug 462108 and 453545. The DB doesn't know which ones are // signed or not so we need to hardcode them here. @@ -795,7 +165,7 @@ class CollectionsController extends AppController } } $this->publish('addons', $addons); - + // fetch all platforms $this->Platform->unbindFully(); $platforms_all = $this->Platform->findAll(); @@ -804,7 +174,7 @@ class CollectionsController extends AppController $platforms[$pf['Platform']['id']] = $pf['Translation']['name']['string']; } $this->publish('platforms', $platforms); - + // prepare and display view $this->pageTitle = 'Collections' . " :: " . sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); $is_ajax = ($method=='ajax'); @@ -835,7 +205,7 @@ class CollectionsController extends AppController $addons = array(); } $this->publish('addons', $addons); - + $this->set('cssAdd', array('collection-style')); $this->publish('suppressHeader', true, false); $this->publish('suppressLanguageSelector', true, false); @@ -843,235 +213,4 @@ class CollectionsController extends AppController $this->pageTitle = 'Fashion Your Firefox'; } - /*** END Fashion Your Firefox ***/ - - - /*** AJAX Handlers ***/ - - /** - * AJAX action for looking up add-ons to add to a collection - */ - function addonLookup() { - global $valid_status; - - // Rather than change our cake parameter regex, use a normal get var - $name = $_GET['q']; - $this->Amo->clean($name); - - // search conditions - $conditions = array( - 'Addon.status' => $valid_status, - 'Addon.inactive' => 0 - ); - if (mb_strlen($name) >= 4) { // fuzzy matching on long names only - $conditions['Translation.name'] = "LIKE %{$name}%"; - } else { - $conditions['Translation.name'] = $name; - } - - $addons = $this->Addon->findAll($conditions, - array('Addon.id', 'Addon.name'), 'Translation.name'); - if (!empty($addons)) { - foreach ($addons as &$_addon) { - // add icons - $_addon['Addon']['iconpath'] = $this->Image->getAddonIconURL($_addon['Addon']['id']); - } - } else { - $addons = false; - } - - $this->publish('addons', $addons); - $this->render('ajax/addon_lookup', 'ajax'); - } - - /** - * central JSON dispatcher for AJAX requests - */ - function json($action, $additional = '') { - switch ($action) { - case 'nickname': - $json = $this->_checkNickname(); - break; - - case 'user': - $json = $this->_handleUser($additional); - break; - - case 'addon': - $json = $this->_handleAddon($additional); - break; - - default: $json = array(); break; - } - - $this->publish('json', $json, false); - $this->render('json', 'ajax'); - } - - /** - * AJAX: check if collection nickname is already used - */ - function _checkNickname() { - $this->Amo->checkLoggedIn(); // must be logged in - if (empty($this->params['url']['nickname'])) { - return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'nickname')); - } - $nickname = preg_replace(INVALID_COLLECTION_NICKNAME_CHARS, '_', - mb_strtolower(trim($this->params['url']['nickname']))); - if ($nickname != mb_strtolower($this->params['url']['nickname'])) - return array( - 'error' => 1, - 'error_message' => ___('collections_edit_nickname_error'), - 'nickname' => $nickname - ); - - $taken = $this->Collection->isNicknameTaken($this->params['url']['nickname']); - return array( - 'error' => 0, - 'nickname' => $nickname, - 'taken' => (int)$taken - ); - } - - /** - * AJAX: Add / remove user to/from this collection's roles - */ - function _handleUser($action) { - if (empty($action)) return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'action')); - if (empty($this->params['form']['collection_id'])) return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'collection_id')); - - $this->Amo->checkLoggedIn(); // must be logged in - - $collection_id = $this->params['form']['collection_id']; - $role = @$this->params['form']['role']; - $email = @$this->params['form']['email']; - $user_id = @$this->params['form']['user_id']; - - $user = $this->Session->read('User'); - $rights = $this->_getUserRights($user, $collection_id); - if (!$rights['atleast_manager']) return $this->Error->getJSONforError(___('error_access_denied')); - - switch ($action) { - case 'add': - if (empty($email)) return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'email')); - $roles = array('publishers' => COLLECTION_ROLE_PUBLISHER, 'managers' => COLLECTION_ROLE_ADMIN); - if (empty($role)) return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'role')); - - $newuser = $this->User->findByEmail($email, array('id')); - if (!empty($newuser)) { - if ($this->Collection->getUserRole($collection_id, $newuser['User']['id']) === false) { - $this->Collection->addUser($collection_id, $newuser['User']['id'], $roles[$role]); - return array('id' => $newuser['User']['id'], 'email' => $email); - } else { - return $this->Error->getJSONforError(___('error_user_exists')); - } - } else { - return $this->Error->getJSONforError(___('error_user_notfound')); - } - break; - - case 'del': - if (empty($user_id)) - return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'user_id')); - if ($this->Collection->removeUser($collection_id, $user_id) !== false) { - return array('id' => $user_id); - } else { - return $this->Error->getJSONforError(___('error_user_notfound')); - } - break; - - default: - return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'action')); - } - } - - /** - * AJAX: Add / remove add-on to/from this collection - * @param string action one of add, del, savecomment - * @param int collection_id (optional) - * @param string collection_uuid (optional) - * Either collection_id or collection_uuid must be specified, but not both. - * @param int addon_id - */ - function _handleAddon($action) { - global $valid_status; - - if (empty($action)) return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'action')); - if (empty($this->params['form']['addon_id']) || !is_numeric($this->params['form']['addon_id'])) return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'addon_id')); - if (!empty($this->params['form']['collection_id']) && is_numeric($this->params['form']['collection_id'])) { - $collection_id = $this->params['form']['collection_id']; - } elseif (!empty($this->params['form']['collection_uuid']) && - ($collection_id = $this->Collection->getIdForUuidOrNickname($this->params['form']['collection_uuid'])) > 0) { - // no-op - } else { - return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'collection_id')); - } - - $this->Amo->checkLoggedIn(); // must be logged in - - $addon_id = $this->params['form']['addon_id']; - - $user = $this->Session->read('User'); - $rights = $this->_getUserRights($user, $collection_id); - - if (!($rights['writable'] || $rights['isadmin'])) return $this->Error->getJSONforError(___('error_access_denied')); - - switch ($action) { - case 'add': - if ($this->AddonCollection->isAddonInCollection($addon_id, $collection_id)) - return $this->Error->getJSONforError(___('error_addon_exists')); - $addon = $this->Addon->getAddon($addon_id); - if (empty($addon) || !in_array($addon['Addon']['status'], $valid_status)) - return $this->Error->getJSONforError(___('error_addon_notfound')); - if (false !== $this->Collection->addAddonToCollection($collection_id, $user['id'], $addon_id)) { - return array( - 'id' => $addon_id, - 'name' => $addon['Translation']['name']['string'], - 'iconURL' => $this->Image->getAddonIconURL($addon_id), - 'date' => strftime(_('date'), mktime()), - 'publisher' => $this->Html->linkUserFromModel($user) - ); - } else { - return $this->Error->getJSONforError(___('collection_error_saving_addon')); - } - break; - - case 'del': - if (!$rights['isadmin'] && !$rights['atleast_manager']) { - // publisher's own add-on? - $res = $this->AddonCollection->deleteByAddonIdAndCollectionId($addon_id, $collection_id, $user['id']); - } else { - $res = $this->AddonCollection->deleteByAddonIdAndCollectionId($addon_id, $collection_id); - } - if ($res) { - return array('id' => $addon_id); - } else { - return $this->Error->getJSONforError(___('collection_error_deleting_addon')); - } - break; - - case 'savecomment': - if (!isset($this->params['form']['comment'])) return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'comment')); - $comment = strip_tags(trim($this->params['form']['comment'])); - - $addon = $this->AddonCollection->find(array('addon_id'=>$addon_id, 'collection_id'=>$collection_id), array('user_id')); - if (empty($addon)) return $this->Error->getJSONforError(___('error_addon_notfound')); - if (!$rights['isadmin'] && !$rights['atleast_manager']) { - // publisher's own add-on? - if ($addon['AddonCollection']['user_id'] != $user['id']) return $this->Error->getJSONforError(___('error_access_denied')); - } - if (!$this->AddonCollection->setComment($collection_id, $addon_id, $comment)) { - return $this->Error->getJSONforError(___('collection_error_saving_comment')); - } else { - return array( - 'addon_id' => $addon_id, - 'comment' => $comment - ); - } - break; - - default: - return $this->Error->getJSONforError(sprintf(_('error_missing_argument'), 'action')); - } - } } diff --git a/site/app/controllers/compatibility_controller.php b/site/app/controllers/compatibility_controller.php index fbf3cf3..9a9754f 100644 --- a/site/app/controllers/compatibility_controller.php +++ b/site/app/controllers/compatibility_controller.php @@ -68,7 +68,7 @@ class CompatibilityController extends AppController $this->jsAdd = array('compatibility.js'); $this->publish('jsAdd', $this->jsAdd); - $this->layout = 'amo2009'; + $this->layout = 'mozilla'; $this->pageTitle = ___('compatibility_dashboard_center_header', 'Add-on Compatibility Center').' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); $this->publish('expand_categories', true); @@ -91,7 +91,6 @@ class CompatibilityController extends AppController $data = unserialize(file_get_contents(NETAPP_STORAGE.'/compatibility-fx-'.$version.'.serialized')); $this->publish('totals', $data['totals']); - $this->publish('percentages', $this->_percentages($data['totals'])); $this->publish('version', $version); $session = $this->Session->read('User'); @@ -118,7 +117,7 @@ class CompatibilityController extends AppController if ($format == 'ajax') $this->render('report', 'ajax'); else - $this->render('report', 'amo2009'); + $this->render('report', 'mozilla'); } function developers($version = COMPAT_DEFAULT_VERSION, $format = 'html') { @@ -175,7 +174,7 @@ class CompatibilityController extends AppController if ($format == 'ajax') $this->render('developers', 'ajax'); else - $this->render('developers', 'amo2009'); + $this->render('developers', 'mozilla'); } function users($version = COMPAT_DEFAULT_VERSION) { @@ -183,20 +182,6 @@ class CompatibilityController extends AppController $this->render('users'); } - /* Calculate compatibility percentages, making sure they add up to 100. */ - function _percentages($totals) { - $k = array(COMPAT_OTHER, COMPAT_ALPHA, COMPAT_BETA, COMPAT_LATEST); - $percentages = array(); - - foreach ($k as $compat) { - $p = $totals[$compat]['adu'] / $totals['adu95']; - // Round to 1 decimal place. - $percentages[$compat] = round($p * 100, 1); - } - // Put any over/under flow into COMPAT_LATEST, somewhat arbitrary. - $percentages[COMPAT_LATEST] += 100 - array_sum($percentages); - return $percentages; - } } ?> diff --git a/site/app/controllers/components/amo.php b/site/app/controllers/components/amo.php index 92ac9ac..a4c59b7 100644 --- a/site/app/controllers/components/amo.php +++ b/site/app/controllers/components/amo.php @@ -340,7 +340,6 @@ class AmoComponent extends Object { $applicationModel->unbindModel(array('hasAndBelongsToMany' => array('Version'), 'hasMany' => array('Tag'))); $applications = $applicationModel->findAll('Application.supported=1', null, null, null, null, 2); $appvids = array(); - $versions = array(); foreach ($applications as $application) { if (!empty($application['Appversion'])) { $appversions = array(); @@ -668,44 +667,66 @@ class AmoComponent extends Object { * ) */ function getNavCategories() { - global $hybrid_categories, $app_listedtypes, $valid_status; + global $hybrid_categories, $app_listedtypes; if (!empty($this->navCategories)) return $this->navCategories; + if(APP_ID == APP_SUGAR) { + $catlist = array(); + } else // addon type list to be added to regular categories list - // get "Themes" category name - if (!isset($this->controller->Addontype)) { - loadModel('Addontype'); - $this->controller->Addontype = new Addontype(); - } + // @partial translation fallback, 4/23/08 + if (_('nav_category_dicts_langpacks') != 'nav_category_dicts_langpacks') { + $catlist = array( + array('name' => _('nav_category_dicts_langpacks'), + 'type' => ADDON_DICT, + 'cat' => 0, + 'weight' => 0), + array('name' => _('nav_category_themes'), + 'type' => ADDON_THEME, + 'cat' => 0, + 'weight' => 0) + ); + } else { + // get "Themes" category name + if (!isset($this->controller->Addontype)) { + loadModel('Addontype'); + $this->controller->Addontype = new Addontype(); + } - $names = $this->controller->Addontype->getNames(); - $_themes_name = $names[ADDON_THEME]; - - $catlist = array( - array('name' => ___('langtools_header_dicts_and_langpacks'), - 'type' => ADDON_DICT, - 'cat' => 0, - 'weight' => 0), - array('name' => $_themes_name, - 'type' => ADDON_THEME, - 'cat' => 0, - 'weight' => 0) - ); + $_themes_name = $this->controller->Addontype->getName(ADDON_THEME); + + $catlist = array( + array('name' => _('langtools_header_dicts_and_langpacks'), + 'type' => ADDON_DICT, + 'cat' => 0, + 'weight' => 0), + array('name' => $_themes_name, + 'type' => ADDON_THEME, + 'cat' => 0, + 'weight' => 0) + ); + } // add plugins where appropriate + // @partial translation fallback, 4/27/08 if (in_array(ADDON_PLUGIN, $app_listedtypes[APP_ID])) { - $catlist[] = array( - 'name' => ___('addons_plugins_pagetitle'), - 'type' => ADDON_PLUGIN, - 'cat' => 0, - 'weight' => 0, - 'count' => COUNT_ADDON_PLUGIN, - ); + if (_('nav_category_plugins') != 'nav_category_plugins') { + $catlist[] = array( + 'name' => _('nav_category_plugins'), + 'type' => ADDON_PLUGIN, + 'cat' => 0, + 'weight' => 0 + ); + } else { + $catlist[] = array( + 'name' => _('addons_plugins_pagetitle'), + 'type' => ADDON_PLUGIN, + 'cat' => 0, + 'weight' => 0 + ); + } } - - // we are sugar - $catlist = array(); // create two sort arrays that we can use with array_multisort later $_weights = array(); @@ -716,7 +737,7 @@ class AmoComponent extends Object { } // add regular categories to list - $tags = $this->getTags(); + $tags = $this->getTags(APP_ID, $app_listedtypes[APP_SUGAR]); foreach ($tags as $_tag) { /* support hybrid categories */ if (isset($hybrid_categories[APP_ID][$_tag['Tag']['id']])) { @@ -729,26 +750,23 @@ class AmoComponent extends Object { $_name = $_tag['Translation']['name']['string']; $_weight = $_tag['Tag']['weight']; - $_count = $_tag['Tag']['count']; // add item to results array $catlist[] = array( 'name' => $_name, 'type' => $_type, 'cat' => $_cat, - 'weight' => $_weight, - 'count' => $_count + 'weight' => $_weight ); // add item to sort arrays too $_names[] = strtolower($_name); $_weights[] = $_weight; } - + // sort results array by weight and name, then return. array_multisort($_weights, SORT_ASC, SORT_NUMERIC, $_names, SORT_ASC, SORT_STRING, $catlist); - // TODO: Use memcache here? $this->navCategories = $catlist; // cache result for subsequent calls return $catlist; } diff --git a/site/app/controllers/components/audit.php b/site/app/controllers/components/audit.php index 2a2d0b3..aad6936 100644 --- a/site/app/controllers/components/audit.php +++ b/site/app/controllers/components/audit.php @@ -76,11 +76,11 @@ class AuditComponent extends Object { $addonInfo = $this->controller->Addon->findById($log['Eventlog']['changed_id'], null, null, -1); $addon = $this->link($addonInfo['Translation']['name']['string'], '/admin/addons/status/'.$log['Eventlog']['changed_id']); - $entry = sprintf(___('audit_addon_status'), $user, $addon, $status); + $entry = "{$user} changed the status of {$addon} to {$status}"; break; case 'file_recalchash': - $entry = sprintf(___('audit_file_recalchash'), $user, $log['Eventlog']['changed_id']); + $entry = "{$user} recalculated the hash for file {$log['Eventlog']['changed_id']}"; break; case 'application_create': @@ -89,10 +89,10 @@ class AuditComponent extends Object { $application = $this->link($applicationInfo['Translation']['name']['string'], '/admin/applications'); if ($log['Eventlog']['action'] == 'application_create') { - $entry = sprintf(___('audit_application_create'), $user, $application); + $entry = "{$user} created application {$application}"; } elseif ($log['Eventlog']['action'] == 'application_edit') { - $entry = sprintf(___('audit_application_edit'), $user, $application); + $entry = "{$user} edited application {$application}"; } break; @@ -102,10 +102,10 @@ class AuditComponent extends Object { $application = $this->link($applicationInfo['Translation']['name']['string'], '/admin/applications'); if ($log['Eventlog']['action'] == 'appversion_create') { - $entry = sprintf(___('audit_appversion_create'), $user, $log['Eventlog']['added'], $application); + $entry = "{$user} created version {$log['Eventlog']['added']} for {$application}"; } elseif ($log['Eventlog']['action'] == 'appversion_delete') { - $entry = sprintf(___('audit_appversion_delete'), $user, $log['Eventlog']['removed'], $application); + $entry = "{$user} deleted version {$log['Eventlog']['removed']} for {$application}"; } break; @@ -115,15 +115,15 @@ class AuditComponent extends Object { $tag = $this->link($tagInfo['Translation']['name']['string'], '/admin/tags'); if ($log['Eventlog']['action'] == 'tag_create') { - $entry = sprintf(___('audit_tag_create'), $user, $tag); + $entry = "{$user} created tag {$tag}"; } elseif ($log['Eventlog']['action'] == 'tag_edit') { - $entry = sprintf(___('audit_tag_edit'), $user, $tag); + $entry = "{$user} edited category {$tag}"; } break; case 'tag_delete': - $entry = sprintf(___('audit_tag_delete'), $user, $log['Eventlog']['removed'], $log['Eventlog']['changed_id']); + $entry = "{$user} deleted category {$log['Eventlog']['removed']} (ID {$log['Eventlog']['changed_id']})"; break; case 'platform_create': @@ -132,25 +132,25 @@ class AuditComponent extends Object { $platform = $this->link($platformInfo['Translation']['name']['string'], '/admin/platforms'); if ($log['Eventlog']['action'] == 'platform_create') { - $entry = sprintf(___('audit_platform_create'), $user, $platform); + $entry = "{$user} created platform {$platform}"; } elseif ($log['Eventlog']['action'] == 'platform_edit') { - $entry = sprintf(___('audit_platform_edit'), $user, $platform); + $entry = "{$user} edited platform {$platform}"; } break; case 'platform_delete': - $entry = sprintf(___('audit_platform_delete'), $user, $log['Eventlog']['removed'], $log['Eventlog']['changed_id']); + $entry = "{$user} deleted platform {$log['Eventlog']['removed']} (ID {$log['Eventlog']['changed_id']})"; break; case 'feature_add': case 'feature_edit': $featureInfo = $this->controller->Feature->findById($log['Eventlog']['changed_id'], null, null, -1); - $entry = sprintf(___('audit_feature_edit'), $user, $featureInfo['Feature']['locale']); + $entry = "{$user} changed a feature for {$featureInfo['Feature']['locale']} locale"; break; case 'feature_remove': - $entry = sprintf(___('audit_admin_feature_remove'), $user, $log['Eventlog']['removed']); + $entry = "{$user} removed feature {$log['Eventlog']['removed']}"; break; case 'group_create': @@ -159,15 +159,15 @@ class AuditComponent extends Object { $group = $this->link($groupInfo['Group']['name'], '/admin/groups'); if ($log['Eventlog']['action'] == 'group_create') { - $entry = sprintf(___('audit_group_create'), $user, $group); + $entry = "{$user} created group {$group}"; } elseif ($log['Eventlog']['action'] == 'group_edit') { - $entry = sprintf(___('audit_group_edit'), $user, $group); + $entry = "{$user} edited group {$group}"; } break; case 'group_delete': - $entry = sprintf(___('audit_group_delete'), $user, $log['Eventlog']['removed'], $log['Eventlog']['changed_id']); + $entry = "{$user} deleted group {$log['Eventlog']['removed']} (ID {$log['Eventlog']['changed_id']})"; break; case 'group_addmember': @@ -179,13 +179,13 @@ class AuditComponent extends Object { $memberInfo = $this->controller->User->findById($log['Eventlog']['added'], null, null, -1); $member = $this->link($memberInfo['User']['firstname'].' '.$memberInfo['User']['lastname'], '/admin/users/'.$log['Eventlog']['added']); - $entry = sprintf(___('audit_group_addmember'), $user, $member, $group); + $entry = "{$user} added {$member} to group {$group}"; } elseif ($log['Eventlog']['action'] == 'group_removemember') { $memberInfo = $this->controller->User->findById($log['Eventlog']['removed'], null, null, -1); $member = $this->link($memberInfo['User']['firstname'].' '.$memberInfo['User']['lastname'], '/admin/users/'.$log['Eventlog']['removed']); - $entry = sprintf(___('audit_group_removemember'), $user, $member, $group); + $entry = "{$user} removed {$member} from group {$group}"; } break; @@ -195,30 +195,30 @@ class AuditComponent extends Object { $response = $this->link($responseInfo['Translation']['name']['string'], '/admin/responses'); if ($log['Eventlog']['action'] == 'response_create') { - $entry = sprintf(___('audit_response_create'), $user, $response); + $entry = "{$user} created response {$response}"; } elseif ($log['Eventlog']['action'] == 'response_edit') { - $entry = sprintf(___('audit_response_edit'), $user, $response); + $entry = "{$user} edited response {$response}"; } break; case 'response_delete': - $entry = sprintf(___('audit_response_delete'), $user, $log['Eventlog']['removed'], $log['Eventlog']['changed_id']); + $entry = "{$user} deleted response {$log['Eventlog']['removed']} (ID {$log['Eventlog']['changed_id']})"; break; case 'config': - $entry = sprintf(___('audit_config'), $user, $log['Eventlog']['field'], $log['Eventlog']['removed'], $log['Eventlog']['added']); + $entry = "{$user} changed config '{$log['Eventlog']['field']}' from '{$log['Eventlog']['removed']}' to '{$log['Eventlog']['added']}'"; break; case 'user_edit': $userInfo = $this->controller->User->findById($log['Eventlog']['changed_id'], null, null, -1); $userLink = $this->link($userInfo['User']['firstname'].' '.$userInfo['User']['lastname'], '/admin/users/'.$log['Eventlog']['changed_id']); - $entry = sprintf(___('audit_user_edit'), $user, $userLink); + $entry = "{$user} edited {$userLink}'s user information"; break; default: - $entry = sprintf(___('audit_admin_default'), $user, $log['Eventlog']['action'], $log['Eventlog']['changed_id']); + $entry = "{$user} committed unknown admin action {$log['Eventlog']['action']} to ID {$log['Eventlog']['changed_id']}"; break; } break; @@ -228,34 +228,34 @@ class AuditComponent extends Object { switch ($log['Eventlog']['action']) { case 'feature_add': $addonLink = $this->linkTitle($log['Eventlog']['added'], '/addon/'); - $entry = sprintf(___('audit_feature_add'), $user, $addonLink); + $entry = "{$user} added addon {$addonLink} to feature list"; break; case 'feature_remove': $addonLink = $this->linkTitle($log['Eventlog']['removed'], '/addon/'); - $entry = sprintf(___('audit_editor_feature_remove'), $user, $addonLink); + $entry = "{$user} removed addon {$addonLink} from feature list"; break; case 'feature_locale_change': $addonLink = $this->linkTitle($log['Eventlog']['changed_id'], '/addon/'); - $entry = sprintf(___('audit_feature_locale_change'), $user, $addonLink); + $entry = "{$user} changed locales for addon {$addonLink} on feature list"; break; case 'review_approve': - $entry = sprintf(___('audit_review_approve'), $user, $log['Eventlog']['changed_id']); + $entry = "{$user} approved review {$log['Eventlog']['changed_id']}"; break; case 'review_delete': if ($this->controller->SimpleAcl->actionAllowed('Admin', 'logs', $this->controller->Session->read('User'))) { - $entry = sprintf(___('audit_review_delete'), $user, $this->link($log['Eventlog']['changed_id'], "/admin/logs/{$log['Eventlog']['id']}")); + $entry = "{$user} deleted review ".$this->link($log['Eventlog']['changed_id'], "/admin/logs/{$log['Eventlog']['id']}"); } else { - $entry = sprintf(___('audit_review_delete'), $user, $log['Eventlog']['changed_id']); + $entry = "{$user} deleted review {$log['Eventlog']['changed_id']}"; } break; default: - $entry = sprintf(___('audit_editor_default'), $user, $log['Eventlog']['action'], $log['Eventlog']['changed_id']); + $entry = "{$user} committed unknown editor action {$log['Eventlog']['action']} to ID {$log['Eventlog']['changed_id']}"; break; } break; @@ -263,21 +263,21 @@ class AuditComponent extends Object { case 'l10n': switch ($log['Eventlog']['action']) { case 'update_applications': - $entry = sprintf(___('audit_update_applications'), $user, $this->linkTitle($log['Eventlog']['notes'], "/localizers/applications/?userlang=")); + $entry = "{$user} updated application translations for ".$this->linkTitle($log['Eventlog']['notes'], "/localizers/applications/?userlang="); break; case 'update_tags': - $entry = sprintf(___('audit_update_tags'), $user, $this->linkTitle($log['Eventlog']['notes'], "/localizers/tags/?userlang=")); + $entry = "{$user} updated category translations for ".$this->linkTitle($log['Eventlog']['notes'], "/localizers/tags/?userlang="); break; case 'update_platforms': - $entry = sprintf(___('audit_update_platforms'), $user, $this->linkTitle($log['Eventlog']['notes'], "/localizers/platforms/?userlang=")); + $entry = "{$user} updated platform translations for ".$this->linkTitle($log['Eventlog']['notes'], "/localizers/platforms/?userlang="); break; case 'update_blog': - $entry = sprintf(___('audit_update_blog'), $user, $this->linkTitle($log['Eventlog']['notes'], "/localizers/platforms/?userlang=")); + $entry = "{$user} updated blog post translations for ".$this->linkTitle($log['Eventlog']['notes'], "/localizers/platforms/?userlang="); break; default: - $entry = sprintf(___('audit_l10n_default'), $user, $log['Eventlog']['action'], $log['Eventlog']['notes']); + $entry = "{$user} committed unknown action {$log['Eventlog']['action']} for <u>{$log['Eventlog']['notes']}</u>"; break; } break; @@ -285,22 +285,22 @@ class AuditComponent extends Object { case 'security': switch ($log['Eventlog']['action']) { case 'reauthentication_failure': - $entry = sprintf(___('audit_reauthentication_failure'), $user, $log['Eventlog']['notes']); + $entry = "{$user} failed to re-authenticate to access {$log['Eventlog']['notes']}."; break; case 'modify_locked_group': $groupInfo = $this->controller->Group->findById($log['Eventlog']['changed_id'], null, null, -1); $group = $this->link($groupInfo['Group']['name'], '/admin/groups'); - $entry = sprintf(___('audit_modify_locked_group'), $user, $group); + $entry = "{$user} attempted to modify locked group {$group}"; break; case 'modify_other_locale': - $entry = sprintf(___('audit_modify_other_locale'), $user, $log['Eventlog']['notes']); + $entry = "{$user} attempted to modify translations in {$log['Eventlog']['notes']} without permission"; break; default: - $entry = sprintf(___('audit_security_default'), $user, $log['Eventlog']['action'], $log['Eventlog']['changed_id']); + $entry = "{$user} committed unknown security action {$log['Eventlog']['action']} to ID {$log['Eventlog']['changed_id']}"; break; } break; @@ -310,7 +310,7 @@ class AuditComponent extends Object { case 'group_associated': $groupInfo = $this->controller->Group->findById($log['Eventlog']['changed_id'], null, null, -1); $group = $this->link($groupInfo['Group']['name'], '/admin/groups'); - $entry = sprintf(___('audit_group_associated'), $user, $group); + $entry = "{$user} associated themselves with {$group}"; break; } break; diff --git a/site/app/controllers/components/developers.php b/site/app/controllers/components/developers.php index ceb24f8..35becf1 100644 --- a/site/app/controllers/components/developers.php +++ b/site/app/controllers/components/developers.php @@ -231,11 +231,6 @@ class DevelopersComponent extends Object { function detectAddontype($file) { $extension = substr($file['name'], strrpos($file['name'], '.')); switch ($extension) { - case '.xo': - case '.xol': - return ADDON_EXTENSION; - break; - case '.xpi': // Dictionaries have a .dic file in the dictionaries directory $zip = new Archive_Zip($file['tmp_name']); @@ -257,6 +252,14 @@ class DevelopersComponent extends Object { case '.xml': return ADDON_SEARCH; break; + + case '.xo': + return ADDON_ACTIVITY; + break; + + case '.xol': + return ADDON_CONTENT; + break; default: return 0; @@ -476,7 +479,9 @@ class DevelopersComponent extends Object { */ function getAllowedExtensions($addontype) { switch ($addontype) { - case ADDON_EXTENSION: $allowed = array('.xo', '.xol'); + case ADDON_ACTIVITY: $allowed = array('.xo'); + break; + case ADDON_CONTENT: $allowed = array('.xol'); break; case ADDON_EXTENSION: $allowed = array('.xpi'); break; @@ -1166,6 +1171,11 @@ class DevelopersComponent extends Object { ADDON_DICT => $this->controller->Addontype->getName(ADDON_DICT), ADDON_LPAPP => $this->controller->Addontype->getName(ADDON_LPAPP) ); + + global $app_listedtypes; + foreach ($app_listedtypes[APP_SUGAR] as $type) { + $addontypes[$type] = $this->controller->Addontype->getName($type); + } if ($autoDetect == true) { $addontypes[0] = _('devcp_additem_addontype_autodetect'); @@ -1179,81 +1189,5 @@ class DevelopersComponent extends Object { return $addontypes; } - - function getLicenses($version_id=null) { - if ($version_id != null) { - $version = $this->controller->Version->findById($version_id); - $version = $version['Version']; - $license = $this->controller->License->findById($version['license_id']); - } - - // Add 'Please Choose...' only if no license has been selected. - if (!isset($version['license_id'])) { - $licenses['null'] = array( - 'name' => ___('devcp_uploader_please_choose'), - 'selected' => True); - } - - // Grab all the pre-approved licenses. - foreach ($this->controller->License->getNames() as $num => $builtin) { - $licenses['builtin_'.$num] = array( - 'name' => $builtin, - 'selected' => isset($license) && (string)$num === $license['License']['name']); - } - - // The trans array holds translations for all the custom licenses we'll - // be displaying. `other` starts off empty, for creating new licenses. - $trans['other']['text']['en-US'] = ''; - - if ($version_id != null) { - // Find all the custom licenses in use by this add-on. - $q = "SELECT Version.version, Version.license_id - FROM versions AS Version INNER JOIN licenses AS License - ON Version.license_id = License.id - WHERE Version.addon_id = {$version['addon_id']} - AND Version.license_id IS NOT NULL - AND License.name = -1 - GROUP BY License.id - ORDER BY Version.id DESC"; - foreach ($this->controller->Version->execute($q) as $existing) { - $existing = $existing['Version']; - $t = ___('devcp_license_existing'); - $val = 'existing_'.$existing['license_id']; - $licenses[$val] = array( - 'name' => sprintf($t, $version['addon_id'], $existing['version']), - 'selected' => $existing['license_id'] == $version['license_id']); - $trans[$val] = $this->controller->License->getAllTranslations($existing['license_id']); - } - } - - $licenses['other'] = array('name' => ___('devcp_uploader_option_other'), - 'selected' => False); - return array($licenses, $trans); - } - - function saveLicense($licenseData, $text, $params) { - $License = $this->controller->License; - if ($licenseData['name'] != 'null') { - $license = $licenseData['name']; - // If the license is pre-approved, we prefixed the id with builtin_. - if (preg_match('/^builtin_(\d+)$/', $license, $matches)) { - $license_id = $License->getBuiltin($matches[1]); - } else if ($license == 'other' || - preg_match('/^existing_(\d+)$/', $license, $matches)) { - // If it's 'other', we need to create a new license. - if ($license == 'other') { - $data['License']['name'] = -1; - $License->save($data); - $license_id = $License->getLastInsertId(); - } else { - $license_id = $matches[1]; - } - // Save any changed translation text. - $localized['text'] = $text; - $License->saveTranslations($license_id, $params, $localized); - } - return $license_id; - } - } } ?> diff --git a/site/app/controllers/components/editors.php b/site/app/controllers/components/editors.php index 0576751..f7b761f 100644 --- a/site/app/controllers/components/editors.php +++ b/site/app/controllers/components/editors.php @@ -123,9 +123,9 @@ class EditorsComponent extends Object { $authors[] = $user['email']; } } - - $releasenotes = $this->controller->Version->findById($version['Version']['id'], array('Version.releasenotes'), null, -1); + $releasenotes = $this->controller->Version->findById($version['Version']['id'], array('Version.releasenotes'), null, -1); + $emailInfo = array('name' => $addon['Translation']['name']['string'], 'id' => $this->controller->Addon->id, 'reviewer' => $session['firstname'].' '.$session['lastname'], @@ -136,7 +136,7 @@ class EditorsComponent extends Object { ); $this->controller->set('info', $emailInfo); - + if ($data['Approval']['ActionField'] == 'public') { if (SITE_RELEASE_EMAIL) { $this->controller->Email->template = 'email/aslo/release'; @@ -145,7 +145,7 @@ class EditorsComponent extends Object { $this->controller->Email->send(false, SITE_RELEASE_EMAIL); } } - + if ($data['Approval']['ActionField'] != 'superreview') { $this->controller->Email->template = 'email/nominated/'.$data['Approval']['ActionField']; $this->controller->Email->to = $emailInfo['email']; @@ -281,7 +281,7 @@ class EditorsComponent extends Object { 'releasenotes' => $releasenotes[0]['releasenotes'] ); $this->controller->set('info', $emailInfo); - + if ($data['Approval']['ActionField'] == 'public') { if (SITE_RELEASE_EMAIL) { $this->controller->Email->template = 'email/aslo/release'; @@ -290,7 +290,7 @@ class EditorsComponent extends Object { $this->controller->Email->send(false, SITE_RELEASE_EMAIL); } } - + if ($data['Approval']['ActionField'] != 'superreview') { $this->controller->Email->template = 'email/pending/'.$data['Approval']['ActionField']; $this->controller->Email->to = $emailInfo['email']; @@ -366,20 +366,22 @@ class EditorsComponent extends Object { function redirectByQueueRank($listtype, $rank) { switch($listtype) { case 'nominated': + $addon = $this->controller->Addon->findAll(array('Addon.status' => STATUS_NOMINATED), + array('Addon.id'), null, 1, $rank); + if (!empty($addon)) { + $addon = $this->controller->Addon->getAddon($addon[0]['Addon']['id'], array('latest_version')); + if (!empty($addon['Version'])) { + $this->controller->redirect("/editors/review/{$addon['Version'][0]['id']}?num={$rank}"); + return; + } + } + break; + case 'pending': - $rank = intval($rank); - $offset = ($rank > 0) ? $rank - 1 : 0; - $sql = $this->buildQueueFilterQuery($listtype); - $queue_sql = "SELECT `Version`.`id` - {$sql['FROM']} - {$sql['JOIN']} - {$sql['WHERE']} - {$sql['ORDER']} - LIMIT 1 OFFSET {$offset}"; - - if ($result = $this->controller->Addon->query($queue_sql)) { - $review_id = $result[0]['Version']['id']; - $this->controller->redirect("/editors/review/{$review_id}?num={$rank}"); + $file = $this->controller->File->findAll(array('File.status'=>STATUS_PENDING), + array('File.version_id'), null, 1, $rank); + if (!empty($file)) { + $this->controller->redirect("/editors/review/{$file[0]['File']['version_id']}?num={$rank}"); return; } break; @@ -489,361 +491,6 @@ class EditorsComponent extends Object { $this->controller->Email->subject = sprintf('[RELEASE] %s-%s', $emailInfo['name'], $emailInfo['version']); $this->controller->Email->send(false, SITE_RELEASE_EMAIL); } - - } - - /** - * Determine if the specified queue is filterable - * Queues currently filterable are: 'nominated' and 'pending' - * @param string $queue name of queue - * @return bool - */ - function isFilterableQueue($queue) { - return in_array($queue, array('pending', 'nominated')); - } - - /** - * Build query components for filtering the specified queue - * Returns empty array for unfilterable queues - * @param string $queue name of queue - * @return array array('FROM'=>string, 'JOIN'=>string, 'WHERE'=>string, 'ORDER'=>string) - * @TODO: for cake >=1.2, return compatible 'joins' and 'conditions' arrays - */ - function buildQueueFilterQuery($queue='pending') { - if (!$this->isFilterableQueue($queue)) { - return array(); - } - - // Setup query components - $base_components = $this->baseQueueFilterQuery($queue); - $from = $base_components['FROM']; - $joins = $base_components['JOIN']; - $where = $base_components['WHERE']; - $order = 'ORDER BY'; - - // Fetch and apply filter - if ($filter = $this->getQueueFilter($queue)) { - $this->controller->Amo->clean($filter, false); - - if (isset($filter['AddonOrAuthor']) && strlen($filter['AddonOrAuthor']) > 0) { - // search addons.name (localized), addons.supportemail (localized) and users.email - $where .= "\nAND (`Version`.`addon_id` IN( - SELECT `a`.`id` FROM `addons` AS `a` - LEFT JOIN `translations` AS `ntr_l` ON - (`ntr_l`.`id`=`a`.`name` AND `ntr_l`.`locale`='".LANG."') - LEFT JOIN `translations` AS `ntr_en` ON - (`ntr_en`.`id`=`a`.`name` AND `ntr_en`.`locale`=`a`.`defaultlocale`) - LEFT JOIN `translations` AS `etr_l` ON - (`etr_l`.`id`=`a`.`supportemail` AND `etr_l`.`locale`='".LANG."') - LEFT JOIN `translations` AS `etr_en` ON - (`etr_en`.`id`=`a`.`supportemail` AND `etr_en`.`locale`=`a`.`defaultlocale`) - WHERE - IFNULL(`ntr_l`.`localized_string`, `ntr_en`.`localized_string`) - LIKE '%{$filter['AddonOrAuthor']}%' - OR IFNULL(`etr_l`.`localized_string`, `etr_en`.`localized_string`) - LIKE '%{$filter['AddonOrAuthor']}%' - - ) OR `Version`.`addon_id` IN( - SELECT `a`.`id` FROM `addons` AS `a` - LEFT JOIN `addons_users` AS `au` ON (`a`.`id`=`au`.`addon_id`) - LEFT JOIN `users` AS `u` ON (`au`.`user_id`=`u`.`id`) - WHERE - `au`.`role` IN(".AUTHOR_ROLE_ADMINOWNER."," - .AUTHOR_ROLE_ADMIN."," - .AUTHOR_ROLE_OWNER."," - .AUTHOR_ROLE_DEV.") - AND `u`.`email` LIKE '%{$filter['AddonOrAuthor']}%' - ))"; - } - - if (!empty($filter['Application'])) { - $joins .= "\nLEFT JOIN `applications_versions` - ON (`Version`.`id`=`applications_versions`.`version_id`)"; - $where .= "\nAND ( - `applications_versions`.`application_id`='{$filter['Application']}')"; - - if (!empty($filter['MaxVersion'])) { - $where .= "\nAND (`applications_versions`.`max`='{$filter['MaxVersion']}')"; - } - } - - if (!empty($filter['SubmissionAge'])) { - $age = $filter['SubmissionAge']; - $age_op = '='; // exact match by default - - if (substr($age, -1) == '+') { - // values like '10+' magically turn into a '>=10' comparison - $age_op = '>='; - $age = substr($age, 0, -1); - } elseif ($age === '1') { - // make '1 day' include less than 1 as well - $age_op = '<='; - } - - if ($queue == 'pending') { - $where .= "\nAND (TIMESTAMPDIFF(DAY, `Version`.`created`, NOW()){$age_op}'{$age}')"; - } elseif ($queue == 'nominated') { - $where .= "\nAND (TIMESTAMPDIFF(DAY, `Addon`.`nominationdate`, NOW()){$age_op}'{$age}')"; - } - } - - if (!empty($filter['Addontype'])) { - if (is_array($filter['Addontype'])) { - $filter_vals = $filter['Addontype']; - } else { - $filter_vals = array($filter['Addontype']); - } - - $where .= "\nAND (`Addon`.`addontype_id` IN('" - . implode("','", $filter_vals) ."'))"; - } - - if (!empty($filter['Platform'])) { - if (is_array($filter['Platform'])) { - $filter_vals = $filter['Platform']; - } else { - $filter_vals = array($filter['Platform']); - } - - // only available to pending queue for now - if ($queue == 'pending') { - $where .= "\nAND (`File`.`platform_id` IN('" - . implode("','", $filter_vals) ."'))"; - } - } - } - //End apply filter - - // Sorting - $theSort = $this->getQueueSort($queue); - switch ($theSort['sortby']) { - case 'name': - $joins .= "\nLEFT JOIN `translations` AS `tr_l` ON - (`tr_l`.`id` = `Addon`.`name` AND `tr_l`.`locale` = '".LANG."')" - ."\nLEFT JOIN `translations` AS `tr_en` ON - (`tr_en`.`id` = `Addon`.`name` AND `tr_en`.`locale` = `Addon`.`defaultlocale`)"; - $order .= ' IFNULL(tr_l.localized_string, tr_en.localized_string)'; - $order .= strtoupper($theSort['direction'] == 'DESC') ? ' DESC' : ' ASC'; - break; - - case 'type': - $joins .= "\nLEFT JOIN addontypes AS `Addontype` ON - (`Addon`.`addontype_id`=`Addontype`.`id`)" - ."\nLEFT JOIN translations AS `tr_l` ON - (`tr_l`.`id` = `Addontype`.`name` AND `tr_l`.`locale` = '".LANG."')" - ."\nLEFT JOIN translations AS tr_en ON - (`tr_en`.`id` = `Addontype`.`name` AND `tr_en`.`locale` = 'en-US')"; - $order .= ' IFNULL(`tr_l`.`localized_string`, `tr_en`.`localized_string`)'; - $order .= strtoupper($theSort['direction'] == 'DESC') ? ' DESC' : ' ASC'; - // secondary sort by age - if ($queue == 'pending') { - $order .= ', `Version`.`created` ASC'; - } else { - $order .= ', `Addon`.`nominationdate` ASC'; - } - break; - - case 'age': - default: - if ($queue == 'pending') { - $order .= ' `Version`.`created`'; - } else { - $order .= ' `Addon`.`nominationdate`'; - } - $order .= strtoupper($theSort['direction'] == 'DESC') ? ' DESC' : ' ASC'; - break; - } - - return array('FROM'=>$from, 'JOIN'=>$joins, 'WHERE'=>$where, 'ORDER'=>$order); - } - - /** - * Return base query components for filtering the specified queue - * Returns empty array for unfilterable queues - * @param string $queue name of queue - * @return array array('FROM'=>string, 'JOIN'=>string, 'WHERE'=>string) - * @TODO: for cake >=1.2, return compatible 'joins' and 'conditions' arrays - */ - function baseQueueFilterQuery($queue='pending') { - if (!$this->isFilterableQueue($queue)) { - return array(); - } - - // Setup query components - if ($queue == 'pending') { - $from = "FROM `files` AS `File`"; - $joins = "LEFT JOIN `versions` AS `Version` ON (`File`.`version_id`=`Version`.`id`)"; - $joins .= "\nINNER JOIN `addons` AS `Addon` ON (`Version`.`addon_id`=`Addon`.`id`)"; - $where = "WHERE `File`.`status`='".STATUS_PENDING."'"; - - } elseif ($queue == 'nominated') { - $from = "FROM `addons` AS `Addon`"; - $joins = "INNER JOIN `versions` AS `Version` ON (`Addon`.`id`=`Version`.`addon_id`)"; - $where = "WHERE `Addon`.`status`='".STATUS_NOMINATED."'"; - - // This makes sure we get only the most recent version - // "Find the version where no other version exists for the same addon and a greater creation date" - // http://stackoverflow.com/questions/157459/problem-joining-on-the-highest-value-in-mysql-table - $joins .= "\nLEFT JOIN `versions` AS `v2` ON ( - `Version`.`addon_id`=`v2`.`addon_id` AND - `Version`.`created`<`v2`.`created`)"; - $where .= "\nAND (`v2`.`id` IS NULL)"; - } - - return array('FROM'=>$from, 'JOIN'=>$joins, 'WHERE'=>$where); - } - - /** - * Get the active filter for the specified queue - * @param string $queue name of queue - * @return array filter array or empty array if none exists - */ - function getQueueFilter($queue) { - if (!$this->isFilterableQueue($queue)) { - return array(); - } - - $filter = array(); - $queue_filters = $this->controller->Session->read('editor_queue_filters'); - if (isset($queue_filters[$queue])) { - $filter = $queue_filters[$queue]; - } - return $filter; - } - - /** - * Set the active filter for the specified queue - * Only known fields are saved into the filter - * @param string $queue name of queue - * @return array empty array, or valid saved filter - */ - function setQueueFilter($queue, $new_filter) { - if (!$this->isFilterableQueue($queue)) { - return array(); - } - - // known filter fields - $filter_fields = array('Addontype', 'Application', 'MaxVersion', - 'Platform', 'SubmissionAge', 'AddonOrAuthor'); - $filter = array(); - - if (is_array($new_filter)) { - foreach ($new_filter as $k => $val) { - // only save known filter fields - if (!empty($val) && in_array($k, $filter_fields)) { - $filter[$k] = $val; - } - } - } - - // get or create array of filters - $queue_filters = $this->controller->Session->read('editor_queue_filters'); - if (!is_array($queue_filters)) { - $queue_filters = array(); - } - - if ($filter) { - // update filter - $queue_filters[$queue] = $filter; - } else { - // clear filter - unset($queue_filters[$queue]); - } - - // save all filters - $this->controller->Session->write('editor_queue_filters', $queue_filters); - - return $filter; - } - - /** - * Determine if the specified queue is sortable - * @param string $queue name of queue - * @return bool - */ - function isSortableQueue($queue) { - return in_array($queue, array('pending', 'nominated')); - } - - /** - * Get current sort order parameters for specified queue - * @param string $queue name of queue - * @param string $default return default filter instead of saved - defaults to false - * @return array - */ - function getQueueSort($queue, $default=false) { - if (!$this->isSortableQueue($queue)) { - return array(); - } - - $defaults = array( - 'pending' => array('sortby'=>'age', 'direction'=>'ASC'), - 'nominated' => array('sortby'=>'age', 'direction'=>'ASC'), - ); - - if (!$default) { - $queue_sorts = $this->controller->Session->read('editor_queue_sorts'); - if (isset($queue_sorts[$queue])) { - return $queue_sorts[$queue]; - } - } - - return $defaults[$queue]; - } - - /** - * Set sort order parameters on specified queue - * @param string $queue - * @param string $sortby - * @param string $direction - * @return bool - */ - function setQueueSort($queue, $sortby='default', $direction='default') { - if (!$this->isSortableQueue($queue)) { - return array(); - } - - // valid sorts for the queues, each sort containing default directions - $validSorts = array( - 'pending' => array( - 'age' => array('direction'=>'ASC'), - 'type' => array('direction'=>'ASC'), - 'name' => array('direction'=>'ASC'), - ), - 'nominated' => array( - 'age' => array('direction'=>'ASC'), - 'type' => array('direction'=>'ASC'), - 'name' => array('direction'=>'ASC'), - ), - ); - - $queueSorts = $this->controller->Session->read('editor_queue_sorts'); - if (!is_array($queueSorts)) { - $queueSorts = array(); - } - - // reset to default - if ($sortby == 'default') { - unset($queueSorts[$queue]); - $this->controller->Session->write('editor_queue_sorts', $queueSorts); - - // set to a known sort - } elseif (array_key_exists($sortby, $validSorts[$queue])) { - $newSort = $validSorts[$queue][$sortby]; - $newSort['sortby'] = $sortby; - // use default direction unless... - if (in_array(strtoupper($direction), array('DESC', 'ASC'))) { - $newSort['direction'] = strtoupper($direction); - } - $queueSorts[$queue] = $newSort; - $this->controller->Session->write('editor_queue_sorts', $queueSorts); - - // unknown sort - } else { - return false; - } - - return true; } } ?> diff --git a/site/app/controllers/components/email.php b/site/app/controllers/components/email.php index 71f20ac..83708b4 100644 --- a/site/app/controllers/components/email.php +++ b/site/app/controllers/components/email.php @@ -12,7 +12,6 @@ class EmailComponent */ var $from = NOBODY_EMAIL; var $fromName = SITE_NAME; - var $sender = null; //var $smtpUserName = 'username'; // SMTP username //var $smtpPassword = 'password'; // SMTP password //var $smtpHostNames= "smtp1.example.com;smtp2.example.com"; // specify main and backup server @@ -71,14 +70,8 @@ class EmailComponent //$mail->Username = $this->smtpUserName; //$mail->Password = $this->smtpPassword; + $mail->Sender = $this->from; $mail->From = $this->from; - // if "Sender" field is set, add correct Sender header (cf. RFC5322 § 3.6.2) - if (!empty($this->sender)) { - $mail->Sender = $this->sender; - $mail->AddCustomHeader("Sender: {$this->sender}"); - } else { - $mail->Sender = $this->from; - } $mail->FromName = $this->fromName; $mail->AddAddress($this->to, $this->toName ); if ($reply) diff --git a/site/app/controllers/components/image.php b/site/app/controllers/components/image.php index b0b6992..491a05d 100644 --- a/site/app/controllers/components/image.php +++ b/site/app/controllers/components/image.php @@ -77,17 +77,7 @@ class ImageComponent extends Object { $this->renderImage($addon['Addon']['icondata'], $addon['Addon']['icontype']); } - - /** - * Renders the icon for a collection - * @param int $collection_id the add-on ID - */ - function renderCollectionIcon($collection_id) { - $collection = $this->controller->Collection->findById($collection_id, array('icondata', 'icontype'), null, -1); - - $this->renderImage($collection['Collection']['icondata'], $collection['Collection']['icontype']); - } - + /** * Renders the preview or thumbnail for an add-on * @param int $preview_id the preview id @@ -134,25 +124,6 @@ class ImageComponent extends Object { } /** - * Gets the URL for the icon of the specified collection - * @param int $addon_id the add-on id - * @return string the URL - */ - function getCollectionIconURL($collection_id) { - $collection = $this->controller->Collection->findById( - $collection_id, array( - 'icontype', 'collection_type', 'uuid', 'modified' - ), null, -1 - ); - - if (empty($collection['Collection']['icontype'])) { - return "{$this->controller->base}/img/collection.png"; - } else { - return "{$this->controller->base}/en-US/firefox/images/collection_icon/{$collection_id}/".strtotime($collection['Collection']['modified']); - } - } - - /** * Gets the URL for the highlighted preview for an add-on * @param int $addon_id add-on ID * @param string $type type of preview diff --git a/site/app/controllers/components/search.php b/site/app/controllers/components/search.php index 20f25ab..8fae4a5 100644 --- a/site/app/controllers/components/search.php +++ b/site/app/controllers/components/search.php @@ -152,7 +152,6 @@ class SearchComponent extends Object { // fields to search in $fields = array('name', 'summary', 'description'); - $_termarray = array(); // first prepare text terms if (is_string($terms) && !empty($terms)) { @@ -163,6 +162,7 @@ class SearchComponent extends Object { preg_match_all($this->search_term_regex, $terms, $_termmatches); // remove quotes around split terms and sanitize them + $_termarray = array(); foreach ($_termmatches[0] as $term) { $term = trim($term, ' "'); $term = $this->controller->Sanitize->sql($term); @@ -181,12 +181,13 @@ class SearchComponent extends Object { } else { //in this case enumerate all addons. this allows advanced search to act as a filter $text_score = "TRUE"; $boolean_score = "TRUE"; + $_termarray = array(); } // now initialize compoents of SQL query $_selects = $_orderby = $_joins = $_where = array(); - $_orderby[] = '(LOWER(a.name) = \''.$this->controller->Sanitize->sql($terms).'\') DESC'; + $_orderby[] = '(LOWER(a.name) = \''.$terms.'\') DESC'; $_orderby[] = '(a.status='.STATUS_PUBLIC.') DESC'; // show public add-ons first $_orderby[] = "(a.name LIKE '%".implode(' ', $_termarray)."%') DESC"; // sort exact name hits first @@ -231,6 +232,10 @@ class SearchComponent extends Object { $_addon_types = array(ADDON_EXTENSION, ADDON_THEME, ADDON_SEARCH, ADDON_DICT, ADDON_LPAPP, ADDON_LPADDON); } } + + // we are sugar + $_addon_types = $app_listedtypes[APP_SUGAR]; + if (!is_array($_addon_types)) $_addon_types = array($_addon_types); // override the odd-on type if the advanced search parameter $atype sent. Notice am assuming -1 not an add-on type @@ -355,104 +360,5 @@ class SearchComponent extends Object { foreach ($_results as $_result) $_result_ids[] = $_result['a']['id']; return $_result_ids; } - - - /** - * The collection search function. - * - * @param string terms to search for - * @param sort is used to specify sort order - * @param locale controls whether we search within only the current locale and en-US (faster) or all locales - * @return array of collection ids - */ - function searchCollections($terms, $sort='', $locale=false) { - // fields to search in - $fields = array('name', 'description'); - $_termarray = array(); - - // first prepare text terms - if (is_string($terms) && !empty($terms)) { - // UTF-8 aware case-insensitive search - $terms = mb_strtolower($terms, 'UTF-8'); - - // split string into single terms - preg_match_all($this->search_term_regex, $terms, $_termmatches); - - // remove quotes around split terms and sanitize them - foreach ($_termmatches[0] as $term) { - $term = trim($term, ' "'); - $term = $this->controller->Sanitize->sql($term); - if ($term) $_termarray[] = $term; - } - // now strip duplicates - $_termarray = array_unique($_termarray); - $_search_termarray = array(); - foreach ($_termarray as $term) { - if (false !== strpos($term, ' ')) $term = '"'.$term.'"'; // enclose "literal phrases" in quotes - $_search_termarray[] = '+'.$term."*"; - } - - $text_score = " MATCH(c.".implode(', c.',$fields).") AGAINST ('".implode(" ", $_termarray)."')"; - $boolean_score = " MATCH(c.".implode(', c.',$fields).") AGAINST ('".implode(" ", $_search_termarray)."' IN BOOLEAN MODE)"; - } else { //in this case enumerate all collections. this allows advanced search to act as a filter - $text_score = "TRUE"; - $boolean_score = "TRUE"; - } - - // now initialize compoents of SQL query - $_selects = $_orderby = $_where = array(); - - $_orderby[] = '(LOWER(c.name) = \''.$this->controller->Sanitize->sql($terms).'\') DESC'; - $_orderby[] = "(c.name LIKE '%".implode(' ', $_termarray)."%') DESC"; // sort exact name hits first - - if (!$locale) { - $_matches = "(c.locale = '".LANG."' OR c.locale = 'en-US' ) AND "; - } - $_matches .= $boolean_score; - - foreach ($fields as $field) { - // select strings - $_selects[] = "c.".$field; - } - if ($text_score !== "TRUE") { - $_selects[] = $text_score." AS text_score"; - $_orderby[] = 'text_score DESC'; - } - - $_where[] = "`Collection`.`application_id`='".APP_ID."'"; - $_where[] = "`Collection`.`listed`='1'"; - - // sorting - switch($sort) { - case 'all': - array_unshift($_orderby, "`Collection`.`subscribers` DESC"); - break; - case 'weekly': - array_unshift($_orderby, "`Collection`.`weekly_subscribers` DESC"); - break; - case 'monthly': - array_unshift($_orderby, "`Collection`.`monthly_subscribers` DESC"); - break; - case 'newest': - array_unshift($_orderby, "`Collection`.`created` DESC"); - break; - } - - // build and run query - $sql = "SELECT DISTINCT `c`.`id`, " . implode(', ', $_selects) . " - FROM `collections_search_summary` AS `c` - INNER JOIN `collections` AS `Collection` ON (`Collection`.`id` = `c`.`id`) - WHERE {$_matches} - ".(empty($_where) ? '' : 'AND ('.implode(' AND ', $_where).') ')." - ORDER BY ".implode(', ', $_orderby); - $_results = $this->controller->Addon->query($sql, true); - - // return the ids found - $_result_ids = array(); - foreach ($_results as $_result) { - $_result_ids[] = $_result['c']['id']; - } - return $_result_ids; - } } ?> diff --git a/site/app/controllers/components/stats.php b/site/app/controllers/components/stats.php index 3702a3c..21dded6 100644 --- a/site/app/controllers/components/stats.php +++ b/site/app/controllers/components/stats.php @@ -125,10 +125,8 @@ class StatsComponent extends Object { // Update pings per day case 'updatepings': - // Since summing update pings per day isn't as useful as summing - // the other data points, we're taking an average here instead. if ($data = $model->query(" - SELECT `date`, {$date_group_column} AVG(`count`) as `count` + SELECT `date`, {$date_group_column} sum(`count`) as `count` FROM `update_counts` WHERE `addon_id`={$addon_id} AND date != '0000-00-00' GROUP BY `{$date_group_by}` @@ -523,89 +521,5 @@ class StatsComponent extends Object { return $xml; } - - /** - * @return array Array of count results for the current day. - */ - function getDailyStats() { - - $yesterday = date('Y-m-d', strtotime('yesterday')); - $after = $yesterday.' 00:00:00'; - $before = $yesterday.' 23:59:59'; - - $ret = array( - 'nomination' => '', - 'pending' => '', - 'flagged' => '', - 'reviews' => '', - 'dailyAddons' => '', - 'totalAddons' => '', - 'dailyVersions' => '', - 'dailyUsers' => '', - 'dailyImages' => '', - 'dailyDownloads' => '', - 'after' => $after, - 'before' => $before - ); - - $model =& $this->controller->Addon; - - if ($buf = $model->query("SELECT COUNT(*) as nomination FROM addons WHERE status=3", true)) { - $ret['nomination'] = $buf[0][0]['nomination']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as pending FROM files WHERE status=2", true)) { - $ret['pending'] = $buf[0][0]['pending']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as flagged FROM addons WHERE adminreview=1", true)) { - $ret['flagged'] = $buf[0][0]['flagged']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as reviews FROM reviews WHERE editorreview=1", true)) { - $ret['reviews'] = $buf[0][0]['reviews']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as total FROM addons WHERE created >= '{$after}' AND created <= '{$before}'", true)) { - $ret['dailyAddons'] = $buf[0][0]['total']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as total FROM addons", true)) { - $ret['totalAddons'] = $buf[0][0]['total']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as total FROM versions WHERE created >= '{$after}' AND created <= '{$before}'", true)) { - $ret['dailyVersions'] = $buf[0][0]['total']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as total FROM users WHERE created >= '{$after}' AND created <= '{$before}'", true)) { - $ret['dailyUsers'] = $buf[0][0]['total']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as total FROM reviews WHERE created >= '{$after}' AND created <= '{$before}'", true)) { - $ret['dailyReviews'] = $buf[0][0]['total']; - unset($buf); - } - - if ($buf = $model->query("SELECT COUNT(*) as total FROM previews WHERE created >= '{$after}' AND created <= '{$before}'", true)) { - $ret['dailyImages'] = $buf[0][0]['total']; - unset($buf); - } - - if ($buf = $model->query("SELECT SUM(count) as count FROM download_counts WHERE date = '{$yesterday}'", true)) { - $ret['dailyDownloads'] = $buf[0][0]['count']; - unset($buf); - } - - return $ret; - } } ?> diff --git a/site/app/controllers/developers_controller.php b/site/app/controllers/developers_controller.php index f089e63..a77946e 100644 --- a/site/app/controllers/developers_controller.php +++ b/site/app/controllers/developers_controller.php @@ -37,31 +37,11 @@ * ***** END LICENSE BLOCK ***** */ require_once('Archive/Zip.php'); -/** - * Returns $object[$name], or $default if that's not set. - * - * If $name is a string of dot-separated names like 'foo.bar.baz', - * $object['foo']['bar']['baz'] will be returned. If any name - * along the way is not set, $default will be returned. - * - * If you want to fetch a name with embedded dots, look elsewhere. - */ -function getitem($object, $name, $default=null) { - $split = explode('.', $name, 2); - if (count($split) == 2) { - list($a, $b) = $split; - return isset($object[$a]) ? getitem($object[$a], $b, $default) - : $default; - } else { - return isset($object[$name]) ? $object[$name] : $default; - } -} - class DevelopersController extends AppController { var $name = 'Developers'; var $uses = array('Addon', 'Addontype', 'Application', 'Approval', 'Appversion', - 'EditorSubscription', 'Eventlog', 'File', 'License', 'Platform', 'Preview', 'Review', + 'EditorSubscription', 'Eventlog', 'File', 'Platform', 'Preview', 'Review', 'Tag', 'Translation', 'User', 'Version'); var $components = array('Amo', 'Developers', 'Editors', 'Email', 'Error', 'Image', 'Opensearch', 'Rdf', 'Src', 'Versioncompare'); @@ -110,9 +90,6 @@ class DevelopersController extends AppController // Default "My Add-ons" sidebar data $session = $this->Session->read('User'); $this->publish('all_addons', $this->Addon->getAddonsByUser($session['id'])); - - // Include the dev_agreement column on developer pages. - array_push($this->Addon->default_fields, 'dev_agreement'); } /** @@ -235,7 +212,6 @@ class DevelopersController extends AppController */ function _submitAddon() { $this->publish('type', 'new'); - $this->publish('hasAgreement', false); $this->render('uploader'); } @@ -266,17 +242,9 @@ class DevelopersController extends AppController $session = $this->Session->read('User'); $this->Addon->saveAuthor($data['Addon']['id'], $session['id']); - // Save License - $license_id = $this->Developers->saveLicense( - $this->data['License'], - getitem($this->data, 'License.text'), - getitem($this->params, 'form.data.License')); - $this->Addon->saveField('dev_agreement', 1); - // Add Version $this->Version->id = 0; $data['Version']['addon_id'] = $data['Addon']['id']; - $data['Version']['license_id'] = $license_id; $this->Version->save($data['Version']); $data['Version']['id'] = $this->Version->getLastInsertId(); @@ -351,30 +319,13 @@ class DevelopersController extends AppController return $this->Error->getJSONforError(sprintf(___('devcp_update_addon_version_exists_error'), $data['Version']['version'], $this->url('/developers/versions/addfile/'.$vcheck['Version']['id']))); } - // Save License - if ($addon['Addon']['dev_agreement'] == true) { - // If we already have an agreement, we didn't show the license - // picker, so use the previously selected license. - global $valid_status; - $old_id = $this->Version->getVersionByAddonId($addon_id, $valid_status); - $oldVersion = $this->Version->findById($old_id); - $license_id = $oldVersion['Version']['license_id']; - } else { - $license_id = $this->Developers->saveLicense( - $this->data['License'], - getitem($this->data, 'License.text'), - getitem($this->params, 'form.data.License')); - } - $this->Addon->save(array('Addon' => array('id' => $addon_id, - 'dev_agreement' => 1))); - + // Add Version $this->Version->id = 0; $data['Version']['addon_id'] = $addon_id; - $data['Version']['license_id'] = $license_id; $this->Version->save($data['Version']); $version_id = $this->Version->getLastInsertId(); - + // If add-on is public, cancel any pending files if ($addon['Addon']['status'] == STATUS_PUBLIC) { $this->Addon->execute("UPDATE files SET status = ".STATUS_SANDBOX." WHERE files.version_id IN (SELECT id FROM versions WHERE versions.addon_id={$addon_id}) AND files.status = ".STATUS_PENDING); @@ -456,6 +407,7 @@ class DevelopersController extends AppController ); } + function _rmtree($dir) { $dir = "$dir"; @@ -543,71 +495,65 @@ class DevelopersController extends AppController ); } - // we are sugar - if (true) { - $bundle = $addon['File']['details']['path']; - $pathinfo = pathinfo($bundle); - - if ($pathinfo['extension'] == '.xo') { - $ini = $this->_unbundle($bundle, 'activity/activity.info'); - if (isset($ini['error'])) - return $this->Error->getJSONforError($ini['error']); - $ini_file = $ini['manifest']; - - if (!is_array($ini_file)) - return $this->Error->getJSONforError(_('devcp_error_activity_info_not_found')); - if (!isset($ini_file['name'])) - return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_name')); - if (!isset($ini_file['activity_version'])) - return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_activity_version')); - - if (isset($ini_file['bundle_id'])) - $addon['Addon']['guid'] = $ini_file['bundle_id']; - else if (isset($ini_file['service_name'])) - $addon['Addon']['guid'] = $ini_file['service_name']; - else - return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_bundle_id')); + if ($addon['Addon']['addontype_id'] == ADDON_ACTIVITY) { + $ini = $this->_unbundle($addon['File']['details']['path'], 'activity/activity.info'); + if (isset($ini['error'])) + return $this->Error->getJSONforError($ini['error']); + $ini_file = $ini['manifest']; - $addon['Addon']['name'] = $ini_file['name']; - $addon['Addon']['summary'] = $ini_file['name']; - $addon['Version']['version'] = $ini_file['activity_version']; + if (!is_array($ini_file)) + return $this->Error->getJSONforError(_('devcp_error_activity_info_not_found')); + if (!isset($ini_file['name'])) + return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_name')); + if (!isset($ini_file['activity_version'])) + return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_activity_version')); + if (isset($ini_file['bundle_id'])) + $addon['Addon']['guid'] = $ini_file['bundle_id']; + else if (isset($ini_file['service_name'])) + $addon['Addon']['guid'] = $ini_file['service_name']; + else + return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_bundle_id')); + + $addon['Addon']['name'] = $ini_file['name']; + $addon['Addon']['summary'] = $ini_file['name']; + $addon['Version']['version'] = $ini_file['activity_version']; + + } else if ($addon['Addon']['addontype_id'] == ADDON_CONTENT) { + $ini = $this->_unbundle($addon['File']['details']['path'], 'library/library.info'); + if (isset($ini['error'])) + return $this->Error->getJSONforError($ini['error']); + $ini_file = $ini['manifest']; + + if (!is_array($ini_file)) + return $this->Error->getJSONforError(_('devcp_error_activity_info_not_found')); + if (!isset($ini_file['name'])) + return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_name')); + if (!isset($ini_file['long_name'])) + return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_summary')); + if (!isset($ini_file['global_name'])) + return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_bundle_id')); + + $addon['Addon']['name'] = $ini_file['name']; + $addon['Addon']['summary'] = $ini_file['long_name']; + $addon['Addon']['guid'] = $ini_file['global_name']; + + if (!isset($this->data['Addon']['id'])) { + $addon['Version']['version'] = 1; } else { - $ini = $this->_unbundle($bundle, 'library/library.info'); - if (isset($ini['error'])) - return $this->Error->getJSONforError($ini['error']); - $ini_file = $ini['manifest']; - - if (!is_array($ini_file)) - return $this->Error->getJSONforError(_('devcp_error_activity_info_not_found')); - if (!isset($ini_file['name'])) - return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_name')); - if (!isset($ini_file['long_name'])) - return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_summary')); - if (!isset($ini_file['global_name'])) - return $this->Error->getJSONforError(_('devcp_error_activity_info_missing_bundle_id')); - - $addon['Addon']['name'] = $ini_file['name']; - $addon['Addon']['summary'] = $ini_file['long_name']; - $addon['Addon']['guid'] = $ini_file['global_name']; - - if (!isset($this->data['Addon']['id'])) { - $addon['Version']['version'] = 1; - } else { - $addon_id = $this->data['Addon']['id']; - $_addon = $this->Addon->findById($addon_id); - $version = 0; - - if (!empty($_addon)) { - foreach ($_addon['Version'] as $i) { - if ($i['version'] > $version) { - $version = $i['version']; - } + $addon_id = $this->data['Addon']['id']; + $_addon = $this->Addon->findById($addon_id); + $version = 0; + + if (!empty($_addon)) { + foreach ($_addon['Version'] as $i) { + if ($i['version'] > $version) { + $version = $i['version']; } } - - $addon['Version']['version'] = $version + 1; } + + $addon['Version']['version'] = $version + 1; } } else // Parse install.rdf file if not a search plugin @@ -665,11 +611,6 @@ class DevelopersController extends AppController elseif ($addon['Addon']['addontype_id'] == ADDON_SEARCH) { // Get search engine properties $search = $this->Opensearch->parse($addon['File']['details']['path']); - - // There was a parse error, the name was empty, etc. Bad things. - if ($search == null) { - return $this->Error->getJSONforError(___('devcp_verify_search_engine_error','Either the XML is invalid or required fields are missing. Please <a href="https://developer.mozilla.org/en/Creating_OpenSearch_plugins_for_Firefox">read the documentation</a>, verify your add-on, and try again.')); - } $addon['Addon']['name'] = $search->name; $addon['Addon']['summary'] = $search->description; @@ -863,7 +804,7 @@ class DevelopersController extends AppController $supportedApps = array( 0 => array( 'Application' => array( - 'id' => APP_FIREFOX + 'id' => APP_SUGAR ) ) ); @@ -1030,6 +971,7 @@ class DevelopersController extends AppController $criteria['description'] = !empty($addon['Translation']['description']['string']); $criteria['category'] = !empty($addon['Tag']); $criteria['previews'] = !empty($previews); + $criteria['reviews'] = (!empty($reviews) && count($reviews) >= 3) ? true : false; $criteria['prerelease'] = !empty($addon['Addon']['prerelease']) ? false : true; $criteria['application'] = !empty($versions) ? true : false; @@ -1068,7 +1010,6 @@ class DevelopersController extends AppController $addonData = array('status' => STATUS_SANDBOX, 'higheststatus' => STATUS_SANDBOX); $this->Addon->save($addonData); $this->publish('success', true); - if (QUERY_CACHE) $this->Addon->Cache->markListForFlush("addon:{$addon['Addon']['id']}"); return true; } @@ -1111,7 +1052,7 @@ class DevelopersController extends AppController $this->render('addon_status_nominate'); return false; } - $addonData = array('status' => STATUS_NOMINATED, 'nominationmessage' => $this->params['form']['data']['Addon']['nominationmessage'], 'nominationdate' => date('Y-m-d H:i:s')); + $addonData = array('status' => STATUS_NOMINATED, 'nominationmessage' => $this->params['form']['data']['Addon']['nominationmessage']); $this->Addon->save($addonData); $this->publish('success', true); @@ -1226,9 +1167,6 @@ class DevelopersController extends AppController $this->publish('version', $version['Version']['version']); } - $addon = $this->Addon->findById($addon_id, array('Addon.dev_agreement')); - $this->publish('hasAgreement', $addon['Addon']['dev_agreement']); - $this->render('uploader'); } @@ -1351,13 +1289,6 @@ class DevelopersController extends AppController } } - // Save license. - $license_id = $this->Developers->saveLicense( - $this->data['License'], - getitem($this->data, 'Version.License.text'), - getitem($this->params, 'form.data.Version.License')); - $this->Version->saveField('license_id', $license_id); - // flush cached add-on objects if (QUERY_CACHE) $this->Addon->Cache->markListForFlush("addon:{$addon_id}"); @@ -1385,13 +1316,6 @@ class DevelopersController extends AppController // Get all translations $translations = $this->Version->getAllTranslations($version_id); - if (isset($version['Version']['license_id'])) { - $trans = $this->License->getAllTranslations($version['Version']['license_id']); - $translations['license_text'] = $trans['text']; - } else { - $translations['license_text'] = array(); - } - $this->set('translations', $translations); // Other info @@ -1486,9 +1410,7 @@ class DevelopersController extends AppController // flush cached add-on objects if (QUERY_CACHE) $this->Addon->Cache->markListForFlush("addon:{$addon_id}"); - // inform about cache lag, if any of the changes were successful - if (!empty($messages['success'])) $messages['success'][] = ___('devcp_several_hours'); - + $messages['success'][] = ___('devcp_several_hours'); $this->publish('messages', $messages); } @@ -1607,10 +1529,8 @@ class DevelopersController extends AppController return; } // Make sure user has some permissions to view this add-on - $session = $this->Session->read('User'); - $isEditor = $this->SimpleAcl->actionAllowed('Editors', '*', $session); $role = $this->Amo->getAuthorRole($inforequest['Approval']['addon_id']); - if (!$isEditor && empty($role)) $this->Amo->accessDenied(); + if (empty($role)) $this->Amo->accessDenied(); $this->publish('inforequest', $inforequest); @@ -1628,21 +1548,6 @@ class DevelopersController extends AppController if (!empty($this->data)) { $session = $this->Session->read('User'); - //Auto-detect addontype if necessary - if ($this->data['Addon']['addontype_id'] == 0) { - $this->data['Addon']['addontype_id'] = $this->Developers->detectAddontype($this->data['File']['file1']); - $this->publish('autoDetected', $this->Addontype->getName($this->data['Addon']['addontype_id'])); - } - - //Make sure addontype is allowed - $allowedAddonTypes = $this->Developers->getAllowedAddonTypes(false, $this->SimpleAcl->actionAllowed('*', '*', $this->Session->read('User'))); - if (!array_key_exists($this->data['Addon']['addontype_id'], $allowedAddonTypes)) { - $this->Error->addError(_('devcp_error_invalid_addontype')); - } - - //Validate files - $this->Developers->validateFiles(); - // reply submitted $approvalData = array( 'user_id' => $session['id'], @@ -1684,5 +1589,1429 @@ class DevelopersController extends AppController } $this->render(); } + + /** + * Index + */ + function index($addon_id = '') { + $this->Amo->clean($addon_id); + $session = $this->Session->read('User'); + $this->User->id = $session['id']; + + // If requesting a specific add-on, show details for it + if (!empty($addon_id)) { + $this->details($addon_id); + return; + } + + $addon_ids = $this->Addon->getAddonsByUser($session['id']); + $data = array(); + + if (!empty($addon_ids)) { + foreach ($addon_ids as $addon_id => $addon_name) { + $addon = $this->Addon->find("Addon.id={$addon_id}"); + + if (!empty($addon['Version'][0])) { + $files = $this->File->findAll("File.version_id={$addon['Version'][0]['id']}"); + + if (!empty($files)) { + foreach ($files as $file) { + $addon['Version'][0]['File'][] = $file; + } + } + } + + $data['Addon'][$addon_id] = array_merge($addon['Addon'], $addon); + $data['Addon'][$addon_id]['updatepings'] = $this->Addon->getMostRecentUpdatePingCount($addon_id); + + } + } + + $this->publish('data', $data); + $this->publish('approval', $this->Amo->getApprovalStatus()); + $this->publish('platforms', $this->Amo->getPlatformName()); + $this->publish('addontypes', $this->Addontype->getNames()); + } + + /** + * Shows the details of a specific add-on + * @param int $id The add-on id + */ + function details($id) { + $this->Amo->clean($id); + + if (!$this->Amo->checkOwnership($id)) { + $this->flash(_('devcp_error_addon_access_denied'), '/developers/index'); + return; + } + + //Bind necessary models + $this->User->bindFully(); + $this->Addontype->bindFully(); + $this->Version->bindFully(); + $this->Addon->bindFully(); + + $this->Addon->id = intval($id); + + if (!empty($this->data)) { + //Save translated fields (releasenotes) + $this->Developers->saveTranslations($this->data); + $this->flash(_('devcp_comments_updated'), '/developers/details/'.$id); + return; + } + + if(!$addon = $this->Addon->read()) { + $this->flash(_('error_addon_notfound'), '/developers/index'); + return; + } + + if (!empty($addon)) { + if (!empty($addon['Version'])) { + foreach ($addon['Version'] as $v => $version) { + $addon['Version'][$v]['File'] = $this->File->findAll("File.version_id='{$version['id']}'"); + } + } + + if (!empty($addon['Preview'])) { + foreach ($addon['Preview'] as $p => $preview) { + $this->Preview->id = $preview['id']; + $addon['Preview'][$p] = $this->Preview->read(); + } + } + + if (!empty($addon['Tag'])) { + foreach ($addon['Tag'] as $tag) { + $tags[] = $tag['id']; + } + $tags = $this->Tag->findAll("Tag.id IN (".implode(', ', $tags).")"); + if (!empty($tags)) { + foreach ($tags as $tag) { + $addon['Tags'][] = $tag['Translation']['name']['string']; + } + } + } + } + + $this->publish('addon', $addon); + $this->publish('approval', $this->Amo->getApprovalStatus()); + $this->publish('platforms', $this->Amo->getPlatformName()); + $this->publish('addontypes', $this->Addontype->getNames()); + + //Retrieve language arrays from bootstrap. + global $valid_languages, $native_languages; + foreach (array_keys($valid_languages) as $key) { + $languages[$key] = $native_languages[$key]['native']; + + $this->Addon->setLang($key, $this); + $addonL = $this->Addon->read(); + + foreach ($addonL['Translation'] as $field => $translation) { + if ($translation['locale'] == $key) { + $info[$key][$field] = $translation['string']; + } + else { + $info[$key][$field] = ''; + } + } + } + $this->Addon->setLang(LANG, $this); + + //Set up localebox info + $this->set('localebox', array('info' => $info, + 'defaultLocale' => $addon['Addon']['defaultlocale'], + 'languages' => $languages)); + $this->render('details', 'mozilla'); + } + + /** + * Add new version to a new or existing add-on + * @param int $id + */ + function add($id = '') { + $this->Amo->clean($id); + $this->publish('subpagetitle', _('devcp_addon_submit_pagetitle')); + $this->breadcrumbs[_('devcp_addon_submit_pagetitle')] = '/developers/add/'.$id; + $this->publish('breadcrumbs', $this->breadcrumbs); + + //If submissions are disabled, show appropriate error + if ($this->Config->getValue('submissions_disabled') == 1 && !$this->SimpleAcl->actionAllowed('*', '*', $this->Session->read('User'))) { + $this->flash(_('devcp_submissions_disabled'), '/', 3); + return; + } + + //Bind necessary models + $this->User->bindFully(); + $this->Addontype->bindFully(); + $this->Version->bindFully(); + $this->Addon->bindFully(); + + //Get a list of add-on types + $this->publish('addonTypes', $this->Addontype->getNames()); + + //Determine if adding a new addon or new version + if (!empty($this->data['Addon']['newAddon'])) { + if ($this->data['Addon']['newAddon'] == 'false') { + $this->addVars['newAddon'] = false; + } + else { + $this->addVars['newAddon'] = true; + } + $this->Addon->id = $id; + $this->addVars['existing'] = $this->Addon->read(); + } + else { + //This is step 1, so check if the id was passed + if (!empty($id) && $this->Amo->checkOwnership($id)) { + $this->Addon->id = $id; + $this->addVars['existing'] = $this->Addon->read(); + $this->addVars['newAddon'] = false; + } + else { + $this->addVars['newAddon'] = true; + $this->addVars['existing'] = array(); + $id = ''; + } + } + $this->publish('existing', $this->addVars['existing']); + + //Set some view data used in all steps + $this->publish('id', $id); + $this->publish('newAddon', $this->addVars['newAddon']); + if (!empty($this->data['Version']['id'])) { + $this->publish('version', $this->data['Version']['id']); + $this->Version->id = $this->data['Version']['id']; + } + + //Set default locale + if (!empty($this->data['Addon']['defaultlocale'])) { + $this->addVars['defaultLocale'] = $this->data['Addon']['defaultlocale']; + } + elseif (!empty($this->addVars['existing']['Addon']['defaultlocale'])) { + $this->addVars['defaultLocale'] = $this->addVars['existing']['Addon']['defaultlocale']; + } + else { + $this->addVars['defaultLocale'] = LANG; + } + + //Set models to read and save using default locale, which may not be the current locale + $this->Addon->setLang($this->addVars['defaultLocale'], $this); + $this->Version->setLang($this->addVars['defaultLocale'], $this); + + $this->set('defaultLocale', $this->addVars['defaultLocale']); + + //Go to appropriate step + if (isset($_POST['cancel'])) { + $this->_cancelAdd(); + } + elseif (isset($this->data['Addon']['add_step1'])) { + $this->_addStep1(); + } + elseif (isset($this->data['Addon']['add_step2'])) { + $this->_addStep2(); + } + elseif (isset($this->data['Addon']['add_step3'])) { + $this->_addStep3(); + } + elseif (isset($this->data['Addon']['add_step4'])) { + $this->_addStep4(); + } + else { + if ($this->addVars['newAddon'] == true) { + $this->_addStep0(); + } + else { + $this->_addStep1(); + } + } + } + + /** + * Step 0: Agreement + */ + function _addStep0() { + if (isset($this->data['Addon']['add_step0'])) { + if (!empty($_POST['accept'])) { + $this->data = ''; + $this->_addStep1(); + return; + } + } + $this->publish('step', 0); + $this->render('add_step0'); + } + + /** + * Step 1: Upload files/select add-on type + */ + function _addStep1() { + //Check if we are processing Step 1 or just displaying it + if (isset($this->data['Addon']['add_step1'])) { + //Check for model validation first + if (!$this->Addon->validates($this->data)) { + $this->Error->addError(_('error_formerrors')); + $this->validateErrors(); + } + + //Auto-detect addontype if necessary + if ($this->data['Addon']['addontype_id'] == 0) { + $this->data['Addon']['addontype_id'] = $this->Developers->detectAddontype($this->data['File']['file1']); + $this->publish('autoDetected', $this->Addontype->getName($this->data['Addon']['addontype_id'])); + } + + //Make sure addontype is allowed + $allowedAddonTypes = $this->Developers->getAllowedAddonTypes(false, $this->SimpleAcl->actionAllowed('*', '*', $this->Session->read('User'))); + if (!array_key_exists($this->data['Addon']['addontype_id'], $allowedAddonTypes)) { + $this->Error->addError(_('devcp_error_invalid_addontype')); + } + + //Validate files + $this->Developers->validateFiles(); + + //Only proceed if no errors + if ($this->Error->noErrors()) { + //Search plugins do not have install.rdf to parse + if ($this->data['Addon']['addontype_id'] != ADDON_SEARCH) { + + //Iterate through files to make sure they're all valid + //Reverse order so that file 1's info will be used after + for ($f = 3; $f >= 1; $f--) { + if (!empty($this->addVars['file'.$f])) { + //Extract install.rdf + $zip = new Archive_Zip($this->addVars['file'.$f]['path']); + $extraction = $zip->extract(array('extract_as_string' => true, 'by_name' => array('install.rdf'))); + + //Make sure install.rdf is present + if (!empty($extraction)) { + $fileContents = $extraction[0]['content']; + + //Use Rdf Component to parse install.rdf + $manifestData = $this->Rdf->parseInstallManifest($fileContents); + + //Clean manifest data - take THAT, evil-doers! + $this->Amo->clean($manifestData); + + //Validate manifest data + $validate = $this->Developers->validateManifestData($manifestData); + // Anything other than boolean true is an error + if ($validate === true) { + + //Validate target applications + $this->addVars['appversions'] = $this->Developers->validateTargetApplications($manifestData['targetApplication']); + + // If result is a string, it's an error + if (is_string($this->addVars['appversions'])) { + $this->Error->addError($this->addVars['appversions']); + } + + //Make sure GUIDs match in all manifests + if (empty($guid)) { + $guid = $manifestData['id']; + } + elseif ($manifestData['id'] != $guid) { + $this->Error->addError(_('devcp_error_file_guids_dont_match')); + } + } + else { + // There was an error validating manifest data + $this->Error->addError($validate); + } + } + else { + $validAppReference = sprintf(_('devcp_valid_app_reference'), '<a href="'.$this->url('/pages/appversions').'">'._('devcp_valid_app_reference_linktext').'</a>'); + $this->Error->addError(_('devcp_error_index_rdf_notfound').'<br>'.$validAppReference); + } + } + } + + if ($this->Error->noErrors()) { + + //These are arrays by locale + $addonNames = $manifestData['name']; + $addonDesc = $manifestData['description']; + + //In case user said it was a new add-on when it is actually an update + if ($existing = $this->Addon->findAll("Addon.guid='{$manifestData['id']}'")) { + if ($existing[0]['Addon']['status'] != STATUS_NULL && $this->addVars['newAddon'] == true) { + $this->addVars['newAddon'] = false; + $this->publish('newAddon', $this->addVars['newAddon']); + } + + /** + * If user went back to step 1, we know because newAddon is true + * and status is NULL. If that's the case, we make a note so that + * we don't insert things that are already inserted. + */ + if ($existing[0]['Addon']['status'] == STATUS_NULL) { + $this->addVars['skipInserts'] = true; + } + + $this->addVars['existing'] = $existing[0]; + $this->publish('existing', $this->addVars['existing']); + $this->publish('id', $this->addVars['existing']['Addon']['id']); + $this->Addon->id = $this->addVars['existing']['Addon']['id']; + if (!$this->Amo->checkOwnership($this->addVars['existing']['Addon']['id'])) { + $this->flash(_('devcp_error_update_access_denied')); + return; + } + } + elseif ($this->addVars['newAddon'] == false) { + $this->addVars['newAddon'] = true; + $this->publish('newAddon', $this->addVars['newAddon']); + $this->publish('existing', ''); + $this->publish('id', ''); + $this->Addon->id = 0; + } + + //Check for identical versions + if (!empty($this->Addon->id)) { + + for ($f = 1; $f <= 4; $f++) { + if (!empty($this->data['File']['platform_id'.$f])) { + $platformIds[] = $this->data['File']['platform_id'.$f]; + } + } + + //If updating the addon, get any identical version numbers + //Must have WHERE statement this way, otherwise cake doesn't quote properly + if ($versions = $this->Version->findAll("addon_id={$this->Addon->id} AND version='{$manifestData['version']}'")) { + foreach ($versions as $version) { + foreach($version['File'] as $file) { + if (in_array($file['platform_id'], $platformIds) && $file['status'] != STATUS_NULL) { + $this->Error->addError(sprintf(_('devcp_error_identical_version_exists'), $manifestData['version'])); + } + } + } + } + } + } + } + //If it is a search plugin, read the .src file + else { + //Parse the .src file + $src = $this->Src->parse(REPO_PATH.'/temp/'.$this->addVars['file1']['filename']); + if ($src !== false) { + $manifestData['name']['en-US'] = $src; + } + else { + $manifestData = array(); + } + + $this->data['Addon']['platform_id'] = 1; + } + + //If no errors, save + if ($this->Error->noErrors()) { + + //Create the addon entry if new + if ($this->addVars['newAddon'] == true) { + if ($this->data['Addon']['addontype_id'] != ADDON_SEARCH) { + $this->data['Addon']['guid'] = $manifestData['id']; + } + + //If creating new, blacklist status and id. + $addonData = array_merge($this->data['Addon'], (array)$this->addVars['file4']); + $addonData = $this->Amo->filterFields($addonData, array(), + array('status', 'id', 'trusted', 'averagerating', 'weeklydownloads', 'totaldownloads')); + $this->Addon->save($addonData); + + if (empty($this->addVars['skipInserts'])) { + $this->Addon->id = $this->Addon->getLastInsertId(); + + //Insert current user as author so no permissions issues occur + $session = $this->Session->read('User'); + $this->Addon->execute("INSERT INTO addons_users (addon_id, user_id) VALUES({$this->Addon->id}, {$session['id']})"); + } + + $this->addVars['existing'] = $this->data; + $this->publish('id', $this->Addon->id); + } + else { + //If updating, whitelist defaultlocale and icon fields + if (!empty($this->addVars['file4'])) { + $addonData = array_merge($this->data['Addon'], $this->addVars['file4']); + } + else { + $addonData = $this->data['Addon']; + } + $addonData = $this->Amo->filterFields($addonData, + array('addontype_id', 'defaultlocale', 'icondata', 'icontype')); + $this->Addon->save($addonData); + } + + //Create the version entry + $this->data['Version']['addon_id'] = $this->Addon->id; + $this->data['Version']['version'] = (!empty($manifestData['version'])) ? $manifestData['version'] : date('Ymd'); + + //Blacklist id. addon_id should be watched too, but it's already overwritten above + $versionData = $this->Amo->filterFields($this->data['Version'], array(), + array('id')); + $this->Version->save($versionData); + $this->Version->id = $this->Version->getLastInsertId(); + $this->publish('version', $this->Version->id); + + if(!empty($this->addVars['appversions'])) { + //Insert target apps + foreach ($this->addVars['appversions'] as $appversion) { + //Cake doesn't deal with extra fields in HABTM tables, so have to insert manually + $this->Version->execute("INSERT INTO applications_versions (version_id, application_id, min, max) VALUES('{$this->Version->id}', '{$appversion['application_id']}', '{$appversion['min']}', '{$appversion['max']}')"); + } + } + + //Insert files + for ($f = 1; $f <= 3; $f++) { + if (!empty($this->addVars['file'.$f]['filename'])) { + $this->File->id = 0; + $this->addVars['file'.$f]['version_id'] = $this->Version->id; + + //These fields are parsed through the code and shouldn't need filtering + $this->File->save($this->addVars['file'.$f]); + $this->addVars['files'][] = $this->File->getLastInsertId(); + } + } + $this->publish('files', $this->addVars['files']); + + //Show next step + $this->addVars['manifestData'] = $manifestData; + + //If user opted to not review add-on info and all required info is present, + //skip to step 3 + if (!empty($this->data['Addon']['Review']) && $this->Developers->noReviewRequired()) { + $this->_addStep3(); + } + else { + $this->_addStep2(); + } + + return; + } + } + } + + //Prep for view + //Get Platforms list + $this->set('platforms', $this->Amo->getPlatformName()); + + //Default to reviewing add-on information + $this->publish('ReviewInfo', ((isset($this->data['Addon']['Review']) && $this->data['Addon']['Review'] == 1) ? array('value' => '1', 'checked' => 'checked') : array())); + + //Set allowed addontypes. + $allowedAddonTypes = $this->Developers->getAllowedAddonTypes(true, $this->SimpleAcl->actionAllowed('*', '*', $this->Session->read('User'))); + $this->set('allowedAddonTypes', $allowedAddonTypes); + + //Retrieve language arrays from bootstrap. + global $valid_languages, $native_languages; + foreach (array_keys($valid_languages) as $key) { + $languages[$key] = $native_languages[$key]['native']; + } + + if (empty($this->data)) + $this->data = array(); + $this->publish('data', $this->data); + $this->set('errors', $this->Error->errors); + $this->publish('step', 1); + $this->set('languages', $languages); + $this->render('add_step1'); + } + + /** + * Step 2: Add-on wide information + */ + function _addStep2() { + + //Check if we are processing step 2 or just displaying the view for it + if (isset($this->data['Addon']['add_step2'])) { + + //Check for model validation first + if (!$this->Addon->validates($this->data)) { + $this->Error->addError(_('error_formerrors')); + $this->validateErrors(); + } + + //Validate tags/categories if not correcting addontype id + if (!empty($this->data['Addon']['addontype_id']) && $this->data['Addon']['addontype_id'] != $this->addVars['existing']['Addon']['addontype_id']) { + unset($this->data['Tag']); + $this->addVars['showTagsStep3'] = $this->data['Addon']['addontype_id']; + } + elseif ($this->addVars['existing']['Addon']['addontype_id'] != ADDON_SEARCH && empty($this->data['Tag']['noTags'])) { + //Search engines don't have tags + @$this->Developers->validateTags($this->data['Tag']['Tag']); + } + + //Validate users + @$this->Developers->validateUsers($this->data['User']['User']); + + if ($this->Error->noErrors()) { + //Updated Addon + if ($this->addVars['newAddon'] == false) { + $tagData = $this->data['Tag']['Tag']; + unset($this->data['Tag']); + unset($this->Addon->data['Tag']); // Cake-- + $this->Tag->LEGACY_saveCategories($this->Addon->id, $tagData); + + //Update addon - usual addon blacklist + $addonData = $this->Amo->filterFields($this->data, array(), + array('id', 'guid', 'status', 'trusted', 'averagerating', 'weeklydownloads', 'totaldownloads')); + + if (!$this->Addon->save($addonData, false)) { + $this->Error->addError(_('devcp_error_saving')); + } + } + //New Addon + else { + //Check for duplicate names + //@TODO this doesn't really do well with the new translation stuff. + if ($this->Addon->findAllByName($this->data['Addon']['name'])) { + $this->Error->addError(_('devcp_error_addonname_not_unique')); + } + else { + //Insert addon + $addonData = $this->Amo->filterFields($this->data, array(), + array('id', 'guid', 'status', 'trusted', 'averagerating', 'weeklydownloads', 'totaldownloads')); + if (!$this->Addon->save($addonData, false)) { + $this->Error->addError(_('devcp_error_saving')); + } + } + } + } + + if ($this->Error->noErrors()) { + //Getting to this point means there were no errors, so show next step + $this->_addStep3(); + return; + } + + //Prepare for view - use post data (unclean data before publishing to avoid escaped quotes in html) + $info = $this->Amo->unclean($this->data); + + //Checkboxes + $checked = array('value' => '1', 'checked' => 'checked'); + $notChecked = array(); + $info['Addon']['viewsource'] = (!empty($this->data['Addon']['viewsource'])) ? $checked : $notChecked; + $info['Addon']['prerelease'] = (!empty($this->data['Addon']['prerelease'])) ? $checked : $notChecked; + $info['Addon']['sitespecific'] = (!empty($this->data['Addon']['sitespecific'])) ? $checked : $notChecked; + $info['Addon']['externalsoftware'] = (!empty($this->data['Addon']['externalsoftware'])) ? $checked : $notChecked; + } + else { + //Prepare for view - use existing data if !empty, else use parsed manifest + $manifestData = $this->Amo->unclean($this->addVars['manifestData']); + $info = $this->Amo->unclean($this->data); + + $info['Addon']['name'] = (!empty($this->addVars['existing']['Translation']['name']['string']) ? $this->addVars['existing']['Translation']['name']['string'] : (!empty($manifestData['name']['en-US']) ? $manifestData['name']['en-US'] : '')); + $info['Addon']['description'] = (!empty($this->addVars['existing']['Translation']['description']['string']) ? $this->addVars['existing']['Translation']['description']['string'] : (!empty($manifestData['description']['en-US']) ? $manifestData['description']['en-US'] : '')); + $info['Addon']['homepage'] = (!empty($this->addVars['existing']['Translation']['homepage']['string']) ? $this->addVars['existing']['Translation']['homepage']['string'] : (!empty($manifestData['homepageURL']) ? $manifestData['homepageURL'] : '')); + + $info['Addon']['addontype_id'] = !empty($this->addVars['existing']['Addon']['addontype_id']) ? $this->addVars['existing']['Addon']['addontype_id'] : $this->data['Addon']['addontype_id']; + $info['Version']['version'] = !empty($manifestData['version']) ? $manifestData['version'] : ''; + $info['Addon']['summary'] = !empty($this->addVars['existing']['Translation']['summary']['string']) ? $this->addVars['existing']['Translation']['summary']['string'] : ''; + + $info['Addon']['supportemail'] = (!empty($this->addVars['existing']['Translation']['supportemail']['string']) ? $this->addVars['existing']['Translation']['supportemail']['string'] : ''); + $info['Addon']['supporturl'] = (!empty($this->addVars['existing']['Translation']['supporturl']['string']) ? $this->addVars['existing']['Translation']['supporturl']['string'] : (!empty($manifestData['homepageURL']) ? $manifestData['homepageURL'] : '')); + + $info['Addon']['eula'] = !empty($this->addVars['existing']['Translation']['eula']['string']) ? $this->addVars['existing']['Translation']['eula']['string'] : ''; + $info['Addon']['privacypolicy'] = !empty($this->addVars['existing']['Translation']['privacypolicy']['string']) ? $this->addVars['existing']['Translation']['privacypolicy']['string'] : ''; + $info['Addon']['guid'] = (!empty($this->addVars['existing']['Addon']['guid']) ? $this->addVars['existing']['Addon']['guid'] : (!empty($manifestData['id']) ? $manifestData['id'] : '')); + $info['File']['id'] = $this->addVars['files']; + + //Checkboxes + $checked = array('value' => '1', 'checked' => 'checked'); + $notChecked = array(); + $info['Addon']['viewsource'] = (!empty($this->addVars['existing']['Addon']['viewsource'])) ? $checked : $notChecked; + $info['Addon']['prerelease'] = (!empty($this->addVars['existing']['Addon']['prerelease'])) ? $checked : $notChecked; + $info['Addon']['sitespecific'] = (!empty($this->addVars['existing']['Addon']['sitespecific'])) ? $checked : $notChecked; + $info['Addon']['externalsoftware'] = (!empty($this->addVars['existing']['Addon']['externalsoftware'])) ? $checked : $notChecked; + } + + //Get authors in order of post data, existing data, default data + $info['authors'] = @$this->Developers->getAuthors($this->addVars['existing']['User']); + + //Get application_ids + $applicationIds = array(); + if (!empty($this->addVars['appversions'])) { + foreach ($this->addVars['appversions'] as $appversion) { + $applicationIds[] = $appversion['application_id']; + } + } + else { + $version = $this->Version->read(); + foreach ($version['Application'] as $appversion) { + $applicationIds[] = $appversion['id']; + } + } + + $tags = $this->Developers->getTags($this->addVars['existing']['Addon']['addontype_id'], $applicationIds); + $this->set('tags', $tags); + + //Get selected tags in order of post data, existing data, default data + $info['selectedTags'] = @$this->Developers->getSelectedTags($this->addVars['existing']['Tag']); + + //Set allowed addontypes. + $allowedAddonTypes = $this->Developers->getAllowedAddonTypes(false, $this->SimpleAcl->actionAllowed('*', '*', $this->Session->read('User'))); + $this->set('allowedAddonTypes', $allowedAddonTypes); + + $this->publish('info', $info); + $this->set('errors', $this->Error->errors); + $this->publish('step', 2); + $this->render('add_step2'); + } + + /** + * Step 3: Version Information + */ + function _addStep3() { + //Check whether we are processing Step 3 or just displaying it + if (isset($this->data['Addon']['add_step3'])) { + //Check for model validation first + if (!$this->Version->validates($this->data)) { + $this->Error->addError(_('error_formerrors')); + $this->validateErrors(); + } + + if ($this->addVars['newAddon'] == false && empty($this->data['Version']['releasenotes'])) { + $this->Error->addError(_('devcp_error_describe_changes'), 'Version/releasenotes'); + //For some reason, using Version model to invalidate doesn't work... + $this->Addon->invalidate('releasenotes'); + $this->Error->addError(_('error_formerrors')); + } + + if ($this->Error->noErrors()) { + $version = $this->Version->read(); + + $fileUpdates = $this->Developers->moveFiles($version, $this->addVars['existing']['Addon']['addontype_id']); + + if ($this->Error->noErrors()) { + + //Update version + $versionData = $this->Amo->filterFields($this->data['Version'], array(), + array('id', 'addon_id')); + $this->Version->data = ''; + $this->Version->save($versionData); + + //Determine file status + $fileStatus = $this->Developers->determineFileStatus($this->addVars['existing']['Addon']); + + //Update file locations + if (!empty($fileUpdates)) { + foreach ($fileUpdates as $file => $filename) { + $this->File->id = $file; + $filename['status'] = $fileStatus; + $this->File->save($filename); + + // Copy file to rsync area if public + if ($fileStatus == STATUS_PUBLIC) { + $fileInfo = $this->File->read(); + $this->Amo->copyFileToPublic($this->Addon->id, $fileInfo['File']['filename']); + $this->File->save(array('datestatuschanged' => $this->Amo->getNOW())); + } + } + } + + //If add-on is public, cancel any pending files + if ($this->addVars['existing']['Addon']['status'] == STATUS_PUBLIC) { + if (!empty($this->addVars['existing']['Version'])) { + foreach($this->addVars['existing']['Version'] as $version) { + $version = $this->Version->find("Version.id='{$version['id']}'"); + if (!empty($version['File'])) { + foreach ($version['File'] as $file) { + if ($file['status'] == STATUS_PENDING) { + $this->File->id = $file['id']; + $this->File->save(array('status' => STATUS_SANDBOX)); + } + } + } + } + } + } + + //Update addon status if new or incomplete + if ($this->addVars['newAddon'] == true || $this->addVars['existing']['Addon']['status'] == 0) { + $addonData = array('Addon' => array('status' => STATUS_SANDBOX)); + if (!empty($this->data['Tag'])) { + $addonData['Tag'] = $this->data['Tag']; + } + $this->Addon->save($addonData); + } + + //Getting this far means there were no errors, display step 5 + $this->_addStep4(); + return; + } + } + } + + //Prep view + //Pull version info + $info = $this->Version->read(); + + if (!empty($this->addVars['showTagsStep3'])) { + foreach ($info['Application'] as $appversion) { + $applicationIds[] = $appversion['id']; + } + $tags = $this->Developers->getTags($this->addVars['showTagsStep3'], $applicationIds); + $this->publish('tags', $tags); + } + + $info['files'] = $this->File->findAllByVersion_id($this->Version->id); + $info['targetapps'] = $this->Amo->getMinMaxVersions($this->Version->id); + + // Determine compatibility with latest Firefox + $noticeVersion = $this->Config->getValue('firefox_notice_version'); + if (!empty($noticeVersion)) { + if (!empty($info['targetapps'])) { + foreach ($info['targetapps'] as $targetApp) { + if ($targetApp['max']['application_id'] == APP_FIREFOX && + $this->Versioncompare->compareVersions($targetApp['max']['version'], $noticeVersion) == -1) { + $this->publish('showFirefoxVersionNotice', true); + } + } + } + } + + //Get Platforms list + $this->publish('platforms', $this->Amo->getPlatformName()); + + $this->publish('info', $info); + $this->set('errors', $this->Error->errors); + $this->publish('step', 3); + $this->render('add_step3'); + } + + /** + * Step 4: Localization + */ + function _addStep4() { + //Check whether we are processing Step 4 or just displaying it + if (isset($this->data['Addon']['add_step4'])) { + + $this->Developers->saveTranslations($this->data); + + $this->_addStep5(); + return; + } + + //Prep view + + //Retrieve language arrays from bootstrap. + global $valid_languages, $native_languages; + foreach (array_keys($valid_languages) as $key) { + $languages[$key] = $native_languages[$key]['native']; + + $this->Addon->setLang($key, $this); + $addon = $this->Addon->read(); + + foreach ($addon['Translation'] as $field => $translation) { + if ($translation['locale'] == $key) { + $info[$key][$field] = $translation['string']; + } + else { + $info[$key][$field] = ''; + } + } + + $this->Version->useLang = $key; + $version = $this->Version->read(); + + foreach ($version['Translation'] as $field => $translation) { + if ($translation['locale'] == $key) { + $info[$key][$field] = $translation['string']; + } + else { + $info[$key][$field] = ''; + } + } + } + + $localizedFields = array( + 'name' => array( + 'type' => 'input', + 'display' => _('devcp_addon_field_name_displaytitle'), + 'model' => 'Addon', + 'field' => 'name', + 'attributes' => array() + ), + 'homepage' => array( + 'type' => 'input', + 'display' => _('devcp_addon_field_homepage_displaytitle'), + 'model' => 'Addon', + 'field' => 'homepage', + 'attributes' => array( + 'size' => 40 + ) + ), + 'summary' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_summary_displaytitle'), + 'model' => 'Addon', + 'field' => 'summary', + 'attributes' => array( + 'rows' => 3, + 'cols' => 55, + 'onBlur' => 'checkSummary(this, \''._('devcp_error_addon_field_summary_toolong').'\');' + ) + ), + 'description' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_description_displaytitle'), + 'model' => 'Addon', + 'field' => 'description', + 'attributes' => array( + 'rows' => 4, + 'cols' => 55 + ) + ), + 'eula' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_eula_displaytitle'), + 'model' => 'Addon', + 'field' => 'eula', + 'attributes' => array( + 'rows' => 6, + 'cols' => 55 + ) + ), + 'privacypolicy' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_privacy_displaytitle'), + 'model' => 'Addon', + 'field' => 'privacypolicy', + 'attributes' => array( + 'rows' => 6, + 'cols' => 55 + ) + ), + 'releasenotes' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_versionnotes_displaytitle'), + 'model' => 'Version', + 'field' => 'releasenotes', + 'attributes' => array( + 'rows' => 3, + 'cols' => 55 + ) + ) + ); + + //Set up localebox info + $this->set('localebox', array('info' => $info, + 'defaultLocale' => $this->addVars['defaultLocale'], + 'languages' => $languages, + 'localizedFields' => $localizedFields)); + $this->publish('step', 4); + $this->render('add_step4'); + } + + /** + * Step 5: Success + */ + function _addStep5() { + //Determine file status + $fileStatus = $this->Developers->determineFileStatus($this->addVars['existing']['Addon']); + + $this->publish('fileStatus', $fileStatus); + $this->publish('step', 5); + $this->render('add_step5'); + } + + /** + * Cancel additem process + */ + function _cancelAdd() { + if (!$this->Amo->checkOwnership($this->Addon->id)) { + $this->flash(_('devcp_error_addon_access_denied'), '/developers/index'); + return; + } + + //Delete version, files, and translations + $this->Developers->deleteVersion($this->Version->id); + + //If this was a new add-on, delete it and translations + if ($this->addVars['newAddon'] == true) { + $this->Developers->deleteAddon($this->Addon->id); + } + + $this->redirect('/developers'); + } + + /** + * Edit add-on + * @param int $id + */ + function edit($id) { + $this->Amo->clean($id); + $this->publish('subpagetitle', _('devcp_addon_edit_pagetitle')); + $this->breadcrumbs[_('devcp_addon_edit_pagetitle')] = '/developers/edit/'.$id; + $this->publish('breadcrumbs', $this->breadcrumbs); + + if (!$this->Amo->checkOwnership($id)) { + $this->flash(_('devcp_error_addon_access_denied'), '/developers/index'); + return; + } + + //Bind necessary models + $this->User->bindFully(); + $this->Addontype->bindFully(); + $this->Version->bindFully(); + $this->Addon->bindFully(); + + $this->Addon->id = $id; + + $this->Amo->detailedLog('Editing add-on'); + + if (!empty($this->data)) { + $this->Amo->detailedLog('Non-empty data'); + $this->Amo->detailedLog('Editing add-on details'); + //Check for model validation first + if (!$this->Addon->validates($this->data)) { + $this->Error->addError(_('error_formerrors')); + $this->validateErrors(); + $this->Amo->detailedLog('Validation errors'); + } + + $addon = $this->Addon->read(); + + //Validate tags/categories + if ($addon['Addon']['addontype_id'] != ADDON_SEARCH && empty($this->data['Tag']['noTags'])) { + @$this->Developers->validateTags($this->data['Tag']['Tag']); + } + + //Validate users + @$this->Developers->validateUsers($this->data['User']['User']); + + $this->Amo->detailedLog('Validated tags and users'); + + //Delete icon if requested + if (!empty($this->data['Addon']['DeleteIcon'])) { + $this->data['Addon']['icontype'] = ''; + $this->data['Addon']['icondata'] = ''; + } + + //Update/insert icon + if (!empty($this->data['Addon']['icon']['name'])) { + $fileErrors = array('1' => ___('devcp_edit_file_error_size'), + '2' => ___('devcp_edit_file_error_size'), + '3' => ___('devcp_edit_file_error_incomplete'), + '4' => ___('devcp_edit_file_error_no') + ); + $allowedImage = array('.png', '.jpg', '.gif'); + + $iconData = $this->Developers->validateIcon($this->data['Addon']['icon'], $fileErrors, $allowedImage); + + if (is_string($iconData)) { + $this->Error->addError($iconData, 'Addon/icon'); + } + else { + $this->data['Addon'] = array_merge($this->data['Addon'], $iconData); + } + } + + if ($this->Error->noErrors()) { + $this->Amo->detailedLog('No errors - performing update'); + //Update addon + $this->Developers->saveUsers($this->data['User']['User']); + $this->Amo->detailedLog('Saved users'); + + //Strip localized fields + $addonData = $this->Developers->stripLocalized($this->data); + $this->Amo->detailedLog('Stripped localized fields: '.print_r($addonData, true), false); + + //usual addon blacklist + if ($this->SimpleAcl->actionAllowed('*', '*', $this->Session->read('User'))) { + $blacklist = array('id', 'guid', 'status'); + } + else { + $blacklist = array('id', 'guid', 'status', 'trusted', 'averagerating', 'weeklydownloads', 'totaldownloads'); + } + $addonData = $this->Amo->filterFields($addonData, array(), $blacklist); + $this->Amo->detailedLog('Fields filtered: '.print_r($addonData, true), false); + $this->Addon->data = array(); + $tagData = $addonData['Tag']['Tag']; + unset($addonData['Tag']['Tag']); + + if ($this->Addon->save($addonData) && $this->Developers->saveTranslations($this->data)) { + $this->Tag->LEGACY_saveCategories($this->Addon->id, $tagData); + $this->Amo->detailedLog('Add-on and translations saved: '.print_r($addonData, true)); + $this->flash(_('devcp_addon_updated_successfully'), '/developers/edit/'.$this->Addon->id); + return; + } + else { + $this->Error->addError(_('devcp_error_saving')); + } + } + } + + if (!$addon = $this->Addon->read()) { + $this->flash(_('error_addon_notfound'), '/developers/index'); + return; + } + + //Get tags based on addontype + $this->Version->id = $addon['Version'][0]['id']; + $applicationIds = array(); + $version = $this->Version->read(); + if (!empty($version)) { + foreach ($version['Application'] as $appversion) { + $applicationIds[] = $appversion['id']; + } + } + $this->set('tags', $this->Developers->getTags($addon['Addon']['addontype_id'], $applicationIds)); + + //Get selected tags + $this->set('selectedTags', $this->Developers->getSelectedTags($addon['Tag'])); + + //Get author info + $addon['authors'] = $this->Developers->getAuthors($addon['User'], false); + + //Retrieve language arrays from bootstrap. + global $valid_languages, $native_languages; + foreach (array_keys($valid_languages) as $key) { + $languages[$key] = $native_languages[$key]['native']; + + $this->Addon->setLang($key, $this); + $addonL = $this->Addon->read(); + + foreach ($addonL['Translation'] as $field => $translation) { + if ($translation['locale'] == $key) { + $info[$key][$field] = $translation['string']; + } + else { + $info[$key][$field] = ''; + } + } + } + $this->Addon->setLang(LANG, $this); + + //Checkboxes + $checked = array('value' => '1', 'checked' => 'checked'); + $notChecked = array(); + $addon['Addon']['viewsource'] = (!empty($addon['Addon']['viewsource'])) ? $checked : $notChecked; + $addon['Addon']['prerelease'] = (!empty($addon['Addon']['prerelease'])) ? $checked : $notChecked; + $addon['Addon']['sitespecific'] = (!empty($addon['Addon']['sitespecific'])) ? $checked : $notChecked; + $addon['Addon']['externalsoftware'] = (!empty($addon['Addon']['externalsoftware'])) ? $checked : $notChecked; + $addon['Addon']['trusted'] = (!empty($addon['Addon']['trusted'])) ? $checked : $notChecked; + + $this->publish('addon', $addon); + $this->set('errors', $this->Error->errors); + $localizedFields = array( + 'name' => array( + 'type' => 'input', + 'display' => _('devcp_addon_field_name_displaytitle'), + 'model' => 'Addon', + 'field' => 'name', + 'attributes' => array() + ), + 'homepage' => array( + 'type' => 'input', + 'display' => _('devcp_addon_field_homepage_displaytitle'), + 'model' => 'Addon', + 'field' => 'homepage', + 'attributes' => array( + 'size' => 40 + ) + ), + 'supportemail' => array( + 'type' => 'input', + 'display' => _('devcp_addon_field_supportemail_displaytitle'), + 'model' => 'Addon', + 'field' => 'supportemail', + 'attributes' => array( + 'size' => 40 + ) + ), + 'supporturl' => array( + 'type' => 'input', + 'display' => _('devcp_addon_field_supporturl_displaytitle'), + 'model' => 'Addon', + 'field' => 'supporturl', + 'attributes' => array( + 'size' => 40 + ) + ), + 'summary' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_summary_displaytitle'), + 'model' => 'Addon', + 'field' => 'summary', + 'attributes' => array( + 'rows' => 3, + 'cols' => 55, + 'onBlur' => 'checkSummary(this, \''._('devcp_error_addon_field_summary_toolong').'\');' + ) + ), + 'description' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_description_displaytitle'), + 'model' => 'Addon', + 'field' => 'description', + 'attributes' => array( + 'rows' => 4, + 'cols' => 55 + ) + ), + 'eula' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_eula_displaytitle'), + 'model' => 'Addon', + 'field' => 'eula', + 'attributes' => array( + 'rows' => 6, + 'cols' => 55 + ) + ), + 'privacypolicy' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_privacy_displaytitle'), + 'model' => 'Addon', + 'field' => 'privacypolicy', + 'attributes' => array( + 'rows' => 6, + 'cols' => 55 + ) + ) + ); + + //Set up localebox info + $this->set('localebox', array('info' => $info, + 'defaultLocale' => $addon['Addon']['defaultlocale'], + 'languages' => $languages, + 'localizedFields' => $localizedFields)); + } + + /** + * Edit a version + * @param int $id + */ + function editversion($id) { + $this->Amo->clean($id); + $this->set ('subpagetitle', _('devcp_version_edit_pagetitle')); + $this->breadcrumbs[_('devcp_version_edit_pagetitle')] = '/developers/editversion/'.$id; + $this->publish('breadcrumbs', $this->breadcrumbs); + + //Bind necessary models + $this->User->bindFully(); + $this->Addontype->bindFully(); + $this->Version->bindFully(); + $this->Addon->bindFully(); + + $this->Version->id = $id; + if (!$version = $this->Version->read()) { + $this->flash(_('error_version_notfound'), '/developers/index'); + return; + } + + if (!$this->Amo->checkOwnership($version['Version']['addon_id'])) { + $this->flash(_('devcp_error_addon_access_denied'), '/developers/index'); + return; + } + + if (!empty($this->data)) { + foreach ($this->data['File']['id'] as $id => $file_id) { + $this->File->id = $file_id; + + //Update or delete file + if ($this->data['File']['Delete'][$id] != 1) { + $fileData = array('platform_id' => $this->data['File']['platform_id'][$id]); + $this->File->save($fileData); + } + else { + $this->Developers->deleteFile($this->File->id, $version['Version']['addon_id']); + } + } + + //Save translated fields (releasenotes) + $this->Developers->saveTranslations($this->data); + + //Save min/max versions + $this->Amo->saveMinMaxVersions($this->Version->id, $this->data['targetApps']); + + //Save other version fields by whitelist + $versionData = $this->Amo->filterFields($this->data['Version'], array('approvalnotes')); + $this->Version->save($versionData); + + $this->flash(_('devcp_version_updated_successfully'), '/developers/editversion/'.$this->Version->id); + return; + } + + $version['targetapps'] = $this->Amo->getMinMaxVersions($this->Version->id); + + // Get all versions + foreach ($version['targetapps'] as $k => $app) { + $_versionlist = $this->Appversion->findAllByApplication_id($app['applications']['id'], '', '', '', '', -1); + $this->Versioncompare->sortAppversionArray($_versionlist); + $version['targetapps'][$k]['appversions'] = $_versionlist; + } + + //Retrieve language arrays from bootstrap. + global $valid_languages, $native_languages; + foreach (array_keys($valid_languages) as $key) { + $languages[$key] = $native_languages[$key]['native']; + + $this->Version->setLang($key, $this); + $versionL = $this->Version->read(); + + foreach ($versionL['Translation'] as $field => $translation) { + if ($translation['locale'] == $key) { + $info[$key][$field] = $translation['string']; + } + else { + $info[$key][$field] = ''; + } + } + } + $this->Version->setLang(LANG, $this); + + //Pull add-on info for translations + $this->Addon->id = $version['Version']['addon_id']; + $addon = $this->Addon->read(); + + $localizedFields = array( + 'releasenotes' => array( + 'type' => 'textarea', + 'display' => _('devcp_addon_field_versionnotes_displaytitle'), + 'model' => 'Version', + 'field' => 'releasenotes', + 'attributes' => array( + 'rows' => 3, + 'cols' => 55 + ) + ) + ); + + $this->publish('version', $version); + $this->publish('addon', $addon); + $this->publish('platforms', $this->Amo->getPlatformName()); + + //Set up localebox info + $this->set('localebox', array('info' => $info, + 'defaultLocale' => $addon['Addon']['defaultlocale'], + 'languages' => $languages, + 'localizedFields' => $localizedFields)); + //Javascript localization + $this->publish('jsLocalization', array( + 'deleteMessage' => _('devcp_question_delete_file') + )); + } + + /** + * Disable or enable add-on + */ + function disable($addon_id) { + $this->Amo->clean($addon_id); + $session = $this->Session->read('User'); + $this->User->id = $session['id']; + + $this->Addon->id = $addon_id; + + // Make sure user has permission + if (!$this->Amo->checkOwnership($addon_id)) { + $this->flash(_('devcp_error_addon_access_denied'), '/developers/index'); + return; + } + + if (!empty($_POST)) { + if (!empty($_POST['enable'])) { + $addonData = array('inactive' => 0); + $this->Addon->save($addonData); + $this->flash(_('devcp_addon_enabled_successfully'), '/developers/details/'.$this->Addon->id); + } + else { + $addonData = array('inactive' => 1); + $this->Addon->save($addonData); + $this->flash(_('devcp_addon_disabled_successfully'), '/developers/details/'.$this->Addon->id); + } + + return; + } + + $addon = $this->Addon->read(); + + $this->publish('addon', $addon); + + $this->render('disable'); + } + + /** + * Nominate an add-on to be public + * @param int $id the add-on id + */ + function nominate($id) { + $this->Amo->clean($id); + $session = $this->Session->read('User'); + $this->User->id = $session['id']; + + $this->publish('subpagetitle', _('devcp_addon_nominate_pagetitle')); + $this->breadcrumbs[_('devcp_addon_nominate_pagetitle')] = '/developers/nominate/'.$id; + $this->publish('breadcrumbs', $this->breadcrumbs); + + $this->User->bindFully(); + $data = $this->User->read(); + + $this->Addon->id = $id; + if (!$addon = $this->Addon->read()) { + $this->flash(_('error_addon_notfound'), '/developers/index'); + return; + } + + //Make sure has ownership or is an editor to nominate + if (!$this->Amo->checkOwnership($id) && !$this->SimpleAcl->actionAllowed('Editors', '*', $this->Session->read('User'))) { + $this->flash(_('devcp_error_addon_access_denied'), '/developers/index'); + return; + } + + //Make sure add-on is in sandbox + if ($addon['Addon']['status'] != STATUS_SANDBOX) { + $this->flash(_('devcp_error_nominate_sandbox_only'), '/developers/details/'.$id, 3); + return; + } + + //Nominate + if (!empty($_POST['nominate'])) { + //Make sure not a pre-release + if ($addon['Addon']['prerelease'] == 1) { + $this->flash(_('devcp_error_nominate_no_prerelease'), '/developers/details/'.$id, 3); + return; + } + + //Make sure nomination message not empty + if (empty($this->data['Addon']['nominationmessage'])) { + $this->flash(_('devcp_error_nominate_message'), '/developers/nominate/'.$id, 3); + return; + } + + //Amo->clean is being stupid. Yes, that's right. I said STUPID + $nominationMessage = str_replace('\n', "\n", $this->data['Addon']['nominationmessage']); + $nominationMessage = str_replace('\r', "\r", $nominationMessage); + + $addonData = array('status' => STATUS_NOMINATED, 'nominationmessage' => $nominationMessage); + + $this->Addon->save($addonData); + $this->flash(_('devcp_addon_nominated_successfully'), '/developers/details/'.$id, 3); + return; + } + + $this->publish('addon', $addon); + } + + /** + * AJAX action for looking up an author by email + * @param string $email + */ + function authorLookup() { + // Rather than change our cake parameter regex, use a normal get var + $email = $_GET['q']; + $this->Amo->clean($email); + if ($authors = $this->User->findAllByEmail($email)) { + $author = $authors[0]['User']['firstname'].' '.$authors[0]['User']['lastname']; + $author .= ' ['.$authors[0]['User']['email'].']'; + $this->publish('id', $authors[0]['User']['id']); + } + else { + $author = false; + } + + $this->publish('author', $author); + $this->publish('email', $email); + $this->render('author_lookup', 'ajax'); + } + } + ?> diff --git a/site/app/controllers/downloads_controller.php b/site/app/controllers/downloads_controller.php index 0057a4d..fc182db 100644 --- a/site/app/controllers/downloads_controller.php +++ b/site/app/controllers/downloads_controller.php @@ -76,11 +76,10 @@ class DownloadsController extends AppController $file_loc = REPO_PATH . '/' . $file_data['Version']['addon_id'] . '/' . $file_data['File']['filename']; // If add-on is in sandbox, make sure sandbox is enabled. If disabled, make sure admin or author. - // If _GET['confirmed'] exists, then a user confirmed a sandbox download via JS, bug 441739 if (($addon_data['Addon']['status'] == STATUS_SANDBOX || $addon_data['Addon']['status'] == STATUS_NOMINATED || $file_data['File']['status'] == STATUS_PENDING) - && !$this->Session->check('User') && !isset($_GET['confirmed'])) { + && !$this->Session->check('User')) { $target_url = str_replace(LANG . '/' . APP_SHORTNAME . '/','',$this->params['url']['url']); $this->redirect('/users/login?to=' . urlencode($target_url) . "&m=1"); @@ -147,17 +146,7 @@ class DownloadsController extends AppController if (!empty($file_id)) { // Use normal download method if file is found - $target = "/downloads/file/{$file_id}/{$type}/{$file_data['File']['filename']}"; - if (count($this->params['url']) > 1) { // re-append query string - $getvars = array(); - foreach ($this->params['url'] as $k => $v) { - if ($k == 'url') continue; - $getvars[] = "$k=$v"; - } - $qs = implode(',', $getvars); - $target .= "?$qs"; - } - $this->redirect($target); + $this->redirect("/downloads/file/{$file_id}/{$type}/{$file_data['File']['filename']}"); } else { // File wasn't found diff --git a/site/app/controllers/editors_controller.php b/site/app/controllers/editors_controller.php index 3a97e51..314f2ef 100644 --- a/site/app/controllers/editors_controller.php +++ b/site/app/controllers/editors_controller.php @@ -23,8 +23,6 @@ * Wil Clouser <clouserw@gmail.com> * Frederic Wenzel <fwenzel@mozilla.com> * Les Orchard <lorchard@mozilla.com> - * Cesar Oliveira <a.sacred.line@gmail.com> - * Scott McCammon <smccammon@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -56,7 +54,6 @@ class EditorsController extends AppController function beforeFilter() { //beforeFilter() is apparently called before components are initialized. Cake++ $this->Amo->startup($this); - $this->Editors->startup($this); $this->Amo->checkLoggedIn(); @@ -67,10 +64,7 @@ class EditorsController extends AppController $this->publish('cssAdd', $this->cssAdd); $this->publish('jsAdd', array('jquery-compressed.js', - 'jquery.autocomplete.pack.js', - 'jquery.tablesorter.min.js', - 'jquery.flot.js', - 'jquery.sparkline.min.js', + 'jquery.autocomplete.js', 'editors')); $this->breadcrumbs = array(_('editors_pagetitle') => '/editors/index'); @@ -85,10 +79,16 @@ class EditorsController extends AppController } //Get counts - $count['pending'] = $this->_getCount('pending'); - $count['nominated'] = $this->_getCount('nominated'); - $count['reviews'] = $this->_getCount('reviews'); + $pending = $this->File->query("SELECT COUNT(*) FROM `files` WHERE `status`=".STATUS_PENDING." GROUP BY `status`"); + $nominated = $this->Addon->query("SELECT COUNT(*) FROM `addons` WHERE `status`=".STATUS_NOMINATED." GROUP BY `status`"); + $reviews = $this->Review->query("SELECT COUNT(*) FROM `reviews` WHERE `editorreview`=1 GROUP BY `editorreview`"); + + $count['pending'] = !empty($pending) ? $pending[0][0]['COUNT(*)'] : 0; + $count['nominated'] = !empty($nominated) ? $nominated[0][0]['COUNT(*)'] : 0; + $count['reviews'] = !empty($reviews) ? $reviews[0][0]['COUNT(*)'] : 0; $this->publish('count', $count); + + $this->count = $count; } /** @@ -143,6 +143,8 @@ class EditorsController extends AppController $this->publish('collapse_categories', true); + $count = $this->count; + $this->Amo->clean($mode); $this->breadcrumbs[_('editors_review_queue_pagetitle')] = '/editors/queue'; $this->publish('breadcrumbs', $this->breadcrumbs); @@ -150,38 +152,72 @@ class EditorsController extends AppController $this->publish('mode', $mode); - // Setup queue filter - if (array_key_exists('filter', $this->params['form'])) { - // set a new filter on this queue - $filter = $this->Editors->setQueueFilter($mode, $this->data['Filter']); - - } elseif (array_key_exists('clear', $this->params['form'])) { - // clear existing filter on this queue - $filter = $this->Editors->setQueueFilter($mode, null); - - // clear sorting - $this->Editors->setQueueSort($mode, 'default'); - - } else { - // fetch existing filter - $filter = $this->Editors->getQueueFilter($mode); - } + if ($mode == 'pending') { + // Setup our pagination + $this->Pagination->total = $count['pending']; + $_pagination_options = array('sortByClass' => 'Version', 'sortBy' => 'created'); - // Handle changes to sorting - if (isset($this->params['url']['sort'])) { - if (isset($this->params['url']['dir'])) { - $this->Editors->setQueueSort($mode, $this->params['url']['sort'], $this->params['url']['dir']); + if (!array_key_exists('show', $_GET) && $this->Session->read('editor_queue_pending_show')) { + $_pagination_options['show'] = $this->Session->read('editor_queue_pending_show'); + } else { + // If $_GET['show'] exists it overrides this in the pagination component + $_pagination_options['show'] = 50; + } + list($_order,$_limit,$_page) = $this->Pagination->init(null, null, $_pagination_options); + $this->Session->write('editor_queue_pending_show', $_limit); + + //Pull any files that have STATUS_PENDING + $pending = $this->File->findAllByStatus(STATUS_PENDING, + array('File.id', 'File.platform_id', 'Version.id', + 'Version.addon_id', 'Version.version', + 'Version.created' + ), $_order, $_limit, $_page, 0); + + if (!empty($pending)) { + foreach ($pending as $k => $file) { + $addon = $this->Addon->findById($file['Version']['addon_id'], + array('Addon.id', 'Addon.name', 'Addon.defaultlocale', + 'Addon.addontype_id', 'Addon.prerelease', + 'Addon.sitespecific', 'Addon.externalsoftware', + )); + $pending[$k] = array_merge_recursive($pending[$k], $addon); + } + } + $addons = $pending; + } + elseif ($mode == 'nominated') { + // Setup our pagination + $this->Pagination->total = $count['nominated']; + $_pagination_options = array('sortByClass' => 'Addon', 'sortBy' => 'created'); + if (!array_key_exists('show', $_GET) && $this->Session->read('editor_queue_nominated_show')) { + $_pagination_options['show'] = $this->Session->read('editor_queue_nominated_show'); } else { - $this->Editors->setQueueSort($mode, $this->params['url']['sort']); + // If $_GET['show'] exists it overrides this in the pagination component + $_pagination_options['show'] = 50; } - } + list($_order,$_limit,$_page) = $this->Pagination->init(null,null,$_pagination_options); + $this->Session->write('editor_queue_nominated_show', $_limit); - // Build the queue - if ($mode == 'pending' || $mode == 'nominated') { - $addons = $this->_buildQueue($mode); + //Pull any add-ons that have STATUS_NOMINATED + $nominated = $this->Addon->findAllByStatus(STATUS_NOMINATED, + array('Addon.id', 'Addon.name', 'Addon.defaultlocale', + 'Addon.addontype_id', 'Addon.prerelease', + 'Addon.sitespecific', 'Addon.externalsoftware', + 'Addon.created' + ), $_order, $_limit, $_page, 0); + if (!empty($nominated)) { + foreach ($nominated as $k => $addon) { + $version = $this->Version->findByAddon_id($addon['Addon']['id'], + array('Version.id', 'Version.addon_id', + 'Version.version', 'Version.modified' + ), 'Version.created DESC'); + $nominated[$k] = array_merge_recursive($nominated[$k], $version); + } + } + $addons = $nominated; } elseif ($mode == 'reviews') { - $this->_reviews($this->_getCount('reviews')); + $this->_reviews($count['reviews']); return; } else { @@ -189,56 +225,141 @@ class EditorsController extends AppController return; } - //Setup filter form fields - $selected = array('Application'=>'', 'MaxVersion'=>'', - 'SubmissionAge'=>'', 'Addontype'=>'', 'Platform'=>'',); - if (is_array($filter)) { - foreach ($filter as $k => $val) { - if (array_key_exists($k, $selected)) { - $selected[$k] = $val; + $platforms = $this->Amo->getPlatformName(); + $applications = $this->Amo->getApplicationName(); + + $submissionTypes = array( 'new' => _('editors_submissiontype_new'), + 'updated' => _('editors_submissiontype_updated') + ); + + //make modifications to the queue array + if (!empty($addons)) { + foreach ($addons as $k => $addon) { + //get min/max versions + if ($targetApps = $this->Amo->getMinMaxVersions($addon['Version']['id'])) { + foreach ($targetApps as $targetApp) { + $appName = $targetApp['translations']['localized_string']; + $addons[$k]['targetApps'][$appName]['min'] = $targetApp['min']['version']; + $addons[$k]['targetApps'][$appName]['max'] = $targetApp['max']['version']; + } + } + + //Age + if ($mode == 'pending') { + $age = time() - strtotime($addon['Version']['created']); + } + elseif ($mode == 'nominated') { + $age = time() - strtotime($addon['Addon']['created']); + } + + //days + if ($age >= (60*60*24*2)) { + $addons[$k]['age'] = sprintf(_('editors_x_days'), floor($age/(60*60*24))); + } + //1 day + elseif ($age >= (60*60*24)) { + $addons[$k]['age'] = _('editors_one_day'); + } + //hours + elseif ($age >= (60*60*2)) { + $addons[$k]['age'] = sprintf(_('editors_x_hours'), floor($age/(60*60))); + } + //hour + elseif ($age >= (60*60)) { + $addons[$k]['age'] = _('editors_one_hour'); + } + //minutes + elseif ($age > 60) { + $addons[$k]['age'] = sprintf(_('editors_x_minutes'), floor($age/60)); + } + //minute + else { + $addons[$k]['age'] = _('editors_one_minute'); + } + + //Generate any additional notes + $addons[$k]['notes'] = array(); + + //Platform-specific? + if (!empty($addon['Version'][0]['File'][0]['platform_id']) && $addon['Version'][0]['File'][0]['platform_id'] != 1) { + $os = array(); + foreach ($addon['Version'][0]['File'] as $file) { + $os[] = $platforms[$file['platform_id']]; + } + $addons[$k]['notes'][] = sprintf(_('editors_platform_x_only'), implode(', ', $os)); + } + elseif (!empty($addon['File']['platform_id']) && $addon['File']['platform_id'] != 1) { + $addons[$k]['notes'][] = sprintf(_('editors_platform_x_only'), $platforms[$addon['File']['platform_id']]); + } + + //Featured? + //@TODO + + //Site specific? + if ($addon['Addon']['sitespecific'] == 1) { + $addons[$k]['notes'][] = _('editors_site_specific'); + } + //Pre-release? + if ($addon['Addon']['prerelease'] == 1) { + $addons[$k]['notes'][] = _('editors_pre-release'); + } + //External software? + if ($addon['Addon']['externalsoftware'] == 1) { + $addons[$k]['notes'][] = _('editors_external_software'); } } } - - $addonOrAuthor = isset($filter['AddonOrAuthor']) ? $filter['AddonOrAuthor'] : ''; - - $maxVersions = array(); - if (!empty($filter['Application'])) { - $app_versions = $this->Appversion->findAllByApplicationId($filter['Application'], - array('Appversion.id', 'Appversion.version'), 'Appversion.version DESC'); - foreach ($app_versions as $av) { - $maxVersions[$av['Appversion']['id']] = $av['Appversion']['version']; + //pr($addons); + //Filters + $selected = array( 'Addontype' => array(), + 'Application' => array(), + 'Platform' => array(), + 'SubmissionType' => array() + ); + $filtered = false; + + if (!empty($this->data['Approval']['Addontype'])) { + foreach ($this->data['Approval']['Addontype'] as $option) { + $selected['Addontype'][$option] = true; } + $filtered = true; } - $submissionAges = array('1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', - '6' => '6', '7' => '7', '8' => '8', '9' => '9', '10+' => '10+'); - $platforms = $this->Amo->getPlatformName(); - $applications = $this->Amo->getApplicationName(); - - $filtered = !empty($filter); - $filterChanged = $filtered && array_key_exists('filter', $this->params['form']); - $filteredCount = $this->_getCount($mode, true); - - $sortOpts = $this->Editors->getQueueSort($mode); + if (!empty($this->data['Approval']['Application'])) { + foreach ($this->data['Approval']['Application'] as $option) { + $selected['Application'][$option] = true; + } + $filtered = true; + } - // raw values for selectTags - $this->set('selected', $selected); + if (!empty($this->data['Approval']['Platform'])) { + foreach ($this->data['Approval']['Platform'] as $option) { + $selected['Platform'][$option] = true; + } + $filtered = true; + } + + if (!empty($this->data['Approval']['SubmissionType'])) { + foreach ($this->data['Approval']['SubmissionType'] as $option) { + $selected['SubmissionType'][$option] = true; + } + $filtered = true; + } + + if (isset($this->data['filter'])) { + $filtered = true; + } + elseif (isset($this->data['clear'])) { + $filtered = false; + } + + $this->publish('addons', $addons); $this->set('platforms', $platforms); $this->set('addontypes', $this->Addontype->getNames()); $this->set('applications', $applications); - $this->set('maxVersions', $maxVersions); - $this->set('submissionAges', $submissionAges); - - $this->publish('addonOrAuthor', $addonOrAuthor); + $this->set('submissionTypes', $submissionTypes); + $this->publish('selected', $selected); $this->publish('filtered', $filtered); - $this->publish('filterChanged', $filterChanged); - $this->publish('filteredCount', $filteredCount); - $this->publish('sortBy', $sortOpts['sortby']); - $this->publish('sortDir', $sortOpts['direction']); - - $this->publish('mode', $mode); - $this->publish('addons', $addons); $this->render('queue'); } @@ -303,8 +424,6 @@ class EditorsController extends AppController } } - $this->pageTitle = $addon['Translation']['name']['string'] . ' :: ' . $this->pageTitle; - if (!empty($addon['Tag'])) { foreach ($addon['Tag'] as $tag) { $tags[] = $tag['id']; @@ -375,10 +494,6 @@ class EditorsController extends AppController } else { $reviewType = 'pending'; } - - // count of filtered queue - $filtered = (true && $this->Editors->getQueueFilter($reviewType)); - $filteredCount = $this->_getCount($reviewType, true); // rank in nomination/update queue if (isset($this->params['url']['num']) && is_numeric($this->params['url']['num'])) @@ -397,8 +512,6 @@ class EditorsController extends AppController $this->publish('history', $history); $this->publish('errors', $this->Error->errors); $this->publish('reviewType', $reviewType, false); - $this->publish('filtered', $filtered); - $this->publish('filteredCount', $filteredCount); $this->render('review'); } @@ -408,7 +521,7 @@ class EditorsController extends AppController * @param int $id The file id */ function file($id) { - $this->Amo->clean($id); + $this->Amo->clean($id); $this->File->id = $id; if (!$file = $this->File->read()) { @@ -434,698 +547,6 @@ class EditorsController extends AppController } exit; } - - - /** - * Performance reports jump off point - * Handles report selection, and user parameter - */ - function performance($mode = '') { - $session = $this->Session->read('User'); - if (!$this->SimpleAcl->actionAllowed('Editor', '*', $session)) { - $this->Amo->accessDenied(); - } - - //Senior Editors can generate reports on anyone - $isSenior = $this->SimpleAcl->actionAllowed('Admin', 'EditAnyAddon', $session); - if ($isSenior && !empty($this->params['url']['user'])) { - $user = $this->User->findByEmail($this->params['url']['user']); - if (empty($user)) { - header('HTTP/1.1 404 Not Found'); - $this->flash(___('editors_performance_user_not_found', 'User not found'), "/editors/performance/{$mode}"); - return; - } - } else { - $user = $this->User->findById($session['id']); - } - - //Chart AJAX - // @TODO: enable this and make charts use ajax to load new data - if (false && $mode == 'chartData') { - $summary = !empty($this->params['url']['sum']) ? $this->params['url']['sum'] : ''; - - if ($summary == 'month') { - $data = $this->_performanceSummaryByMonth($user['User']['id'], 12); - } - elseif ($summary == 'cat') { - $year = null; - $month = null; - if (!empty($this->params['url']['year'])) { - $year = intval($this->params['url']['year']); - } - if (!empty($this->params['url']['month'])) { - $month = intval($this->params['url']['month']); - } - $data = $this->_performanceSummaryByCategory($user['User']['id'], $year, $month); - } - - $this->set('json', $data); - $this->render('ajax/json', 'ajax'); - return; - } - - // display name (or email if not set) - $userName = trim("{$user['User']['firstname']} {$user['User']['lastname']}"); - if ($userName == '') { - $userName = $user['User']['email']; - } - - $this->publish('mode', $mode); - $this->publish('user', $user); - $this->publish('userName', $userName); - $this->publish('showUserLookup', $isSenior); - $this->publish('editors', $isSenior ? $this->_recentEditors() : array()); - $this->publish('collapse_categories', false); - $this->publish('subpagetitle', ___('editors_performance_pagetitle', 'Performance Reports')); - $this->set('page', 'performance'); - - //Standard text report - if ($mode == '') { - $this->_performanceTable($user['User']['id']); - - //Charts - } elseif ($mode == 'charts') { - $this->_performanceCharts($user['User']['id']); - - } else { - $this->redirect('/editors/performance'); - } - return; - } - - - /** - * Generate a detailed performance report with weekly totals and team averages - * @param int $userId user to generate report for - */ - function _performanceTable($userId) { - // Initialize report data - $myApprovals = array(); - $myTotal = 0; - $teamTotal = 0; - $teamSize = 0; - $teamAverage = 0; - $weeklyTotals = array(); - $myMtdTotal = 0; - $teamMtdTotal = 0; - $teamMtdAverage = 0; - $myYtdTotal = 0; - $teamYtdTotal = 0; - $teamYtdSize = 0; - $teamYtdAverage = 0; - - //Default conditions are the current month - $ytdEndTime = strtotime('tomorrow'); - $startDate = date('Y-m-01'); - $startTime = strtotime($startDate); - $endDate = ___('editors_date_filter_placeholder', 'YYYY-MM-DD'); - $endTime = $ytdEndTime; - - //If user has specified own conditions, use those - if (!empty($this->params['url']['start'])) { - $ts = strtotime($this->params['url']['start']); - if ($ts !== false && $ts != -1) { - $startTime = $ts; - $startDate = $this->params['url']['start']; - } - } - if (!empty($this->params['url']['end'])) { - $ts = strtotime($this->params['url']['end']); - if ($ts !== false && $ts != -1) { - $endTime = strtotime('+1 day', $ts); - $endDate = $this->params['url']['end']; - } - } - - //Initialize weekly data - $week = array('from' => $startTime, - 'to' => min(strtotime('next Monday', $startTime), $endTime)-1, - 'myTotal' => 0, 'teamTotal' => 0, 'teamAverage' => 0); - while ($week['from'] < $endTime) { - $weeklyKey = $this->_makeYearWeekKey($week['from']); - $weeklyTotals[$weeklyKey] = $week; - - $week = array('from' => $week['to']+1, - 'to' => min(strtotime('+7 day', $week['to']), $endTime-1), - 'myTotal' => 0, 'teamTotal' => 0, 'teamAverage' => 0); - } - - //Fetch approvals over specified date range - $conditions = array("Approval.created >= FROM_UNIXTIME('{$startTime}')", - "Approval.created < FROM_UNIXTIME('{$endTime}')"); - - $order = '`Approval`.`created` ASC'; - if ($approvals = $this->Approval->findAll($conditions, null, $order)) { - foreach ($approvals as $k => $approval) { - $weeklyKey = $this->_makeYearWeekKey(strtotime($approval['Approval']['created'])); - if (!array_key_exists($weeklyKey, $weeklyTotals)) { - // this should never happen, but better to let there be obvious gaps in - // weekly totals than seemingly legit (but probably bogus) counts - continue; - } - - //Fetch addon details for approvals by the report user - if ($approval['User']['id'] == $userId) { - $approval['Addon'] = $this->Addon->getAddon($approval['Approval']['addon_id'], - array('list_details')); - $myApprovals[] = $approval; - $myTotal++; - $weeklyTotals[$weeklyKey]['myTotal']++; - } - $teamTotal++; - $weeklyTotals[$weeklyKey]['teamTotal']++; - } - } - - /* add formatting to aid in debugging - foreach ($weeklyTotals as $k => $w) { - $weeklyTotals[$k]['formatted'] = - date('Y-m-d H:i:s', $w['from']).' - '.date('Y-m-d H:i:s', $w['to']); - } - pr($weeklyTotals); /**/ - - // YTD and MTD calculations - $sql = "SELECT MONTH(`Approval`.`created`) AS `month`, `User`.`id`, COUNT(*) AS `total` - FROM `approvals` AS `Approval` - LEFT JOIN `users` AS `User` ON (`User`.`id`=`Approval`.`user_id`) - WHERE `Approval`.`created` >= '".date('Y')."-01-01 00:00:00' - AND `Approval`.`created` < FROM_UNIXTIME('{$ytdEndTime}') - GROUP BY `month`, `Approval`.`user_id`"; - if ($results = $this->Approval->query($sql)) { - $thisMonth = date('m'); - foreach ($results as $row) { - $teamYtdTotal += $row[0]['total']; - $teamMtdTotal += ($row[0]['month'] == $thisMonth) ? $row[0]['total'] : 0; - if ($row['User']['id'] == $userId) { - $myYtdTotal += $row[0]['total']; - $myMtdTotal += ($row[0]['month'] == $thisMonth) ? $row[0]['total'] : 0; - } - } - } - - //Calculate averages - $teamSize = $this->_teamSize($endTime); - if ($teamSize) { - $teamAverage = $teamTotal / $teamSize; - foreach ($weeklyTotals as $k => $week) { - $weeklyTotals[$k]['teamAverage'] = $week['teamTotal'] / $teamSize; - } - } - $teamYtdSize = $this->_teamSize($ytdEndTime); - if ($teamYtdSize) { - $teamYtdAverage = $teamYtdTotal / $teamYtdSize; - $teamMtdAverage = $teamMtdTotal / $teamYtdSize; - } - - // Publish and render - $this->publish('startDate', $startDate); - $this->publish('endDate', $endDate); - - $this->publish('addonTypes', $this->Addontype->getNames()); - $this->publish('myApprovals', $myApprovals); - $this->publish('myTotal', $myTotal); - $this->publish('teamAverage', $teamAverage); - $this->publish('weeklyTotals', $weeklyTotals); - $this->publish('myMtdTotal', $myMtdTotal); - $this->publish('teamMtdAverage', $teamMtdAverage); - $this->publish('myYtdTotal', $myYtdTotal); - $this->publish('teamYtdAverage', $teamYtdAverage); - - $this->render('performance_table'); - } - - - /** - * Generate charts showing yearly activity and category breakdowns - * @param int $userId user to generate report for - */ - function _performanceCharts($userId) { - $byMonthData = $this->_performanceSummaryByMonth($userId, 12); - $teamSize = $this->_teamSize(time()); - - // category breakdown can be for an entire year, or a specific - // month and year - $year = date('Y'); - $month = null; - if (!empty($this->params['url']['year'])) { - $year = intval($this->params['url']['year']); - } - if (!empty($this->params['url']['month'])) { - $month = intval($this->params['url']['month']); - } - $byCatData = $this->_performanceSummaryByCategory($userId, $year, $month); - - // points for x-axis labels (javascript) - $monthlyTicks = array(); - foreach ($byMonthData['labels'] as $i => $label) { - $this->_sanitizeArray($label); - $monthlyTicks[] = "[{$i},'{$label}']"; - } - - // points for user activity (javascript) - $userPoints = array(); - foreach ($byMonthData['usercount'] as $i => $n) { - $userPoints[] = "[{$i},{$n}]"; - } - - // points for team activity (javascript) - $teamPoints = array(); - foreach ($byMonthData['teamcount'] as $i => $n) { - $n = $n / $teamSize; - $teamPoints[] = "[{$i},{$n}]"; - } - - // pie chart color-scheme (javascript) - $sliceColors = array("'#6d746a'","'#205f9a'","'#3d8128'","'#63522b'", - "'#dc5313'","'#f3c01c'","'#bc1c39'"); - - // pie chart labels (javascript) - $sliceLabels = array(); - foreach ($byCatData['labels'] as $i => $label) { - $this->_sanitizeArray($label); - $sliceLabels[] = "'{$label}'"; - } - - // pie chart date title - if ($month) { - $pieTitleDate = strftime('%B %Y', mktime(12,0,0,$month,1,$year)); - } else { - $pieTitleDate = $year; - } - - // months for filter select - $monthSelect = array(''=>''); - for ($n = 1; $n<=12; $n++) { - $monthSelect[$n] = strftime('%B', mktime(12,0,0,$n,1)); - } - - $this->set('monthlyTicksJS', implode(',', $monthlyTicks)); - $this->set('monthlyUserPointsJS', implode(',', $userPoints)); - $this->set('monthlyTeamPointsJS', implode(',', $teamPoints)); - $this->set('sliceColorsJS', implode(',', $sliceColors)); - $this->set('sliceLabelsJS', implode(',', $sliceLabels)); - $this->set('userPieDataJS', implode(',', $byCatData['usercount'])); - $this->set('teamPieDataJS', implode(',', $byCatData['teamcount'])); - $this->publish('year', $year); - $this->publish('month', $month); - $this->publish('monthSelect', $monthSelect); - $this->publish('pieTitleDate', $pieTitleDate); - $this->render('performance_charts'); - } - - - /** - * Generate data for monthly summary of user and team activity - * @param int $userId user to generate report for - * @param int $months number of months - * @param int $endMonth last month of report (default current month) - * @param int $endYear year of last month of report (default current year) - * @return array - */ - function _performanceSummaryByMonth($userId, $months=12, $endMonth=null, $endYear=null) { - $user = $this->User->findById($userId); - $data = array( - 'email' => $user['User']['email'], - 'firstname' => $user['User']['firstname'], - 'lastname' => $user['User']['lastname'], - 'labels' => array(), - 'usercount' => array(), - 'teamcount' => array()); - - if (is_null($endMonth)) { - $endMonth = date('n'); - } - if (is_null($endYear)) { - $endYear = date('Y'); - } - - $endTime = strtotime(sprintf('%04d-%02d-01 00:00:00 +1 month', $endYear, $endMonth)); - $startTime = strtotime(sprintf('-%d month', $months), $endTime); - - $sql = "SELECT DATE_FORMAT(`Approval`.`created`, '%Y-%m') AS yearmonth, - `Approval`.`created`, `User`.`id`, COUNT(*) AS `total` - FROM `approvals` AS `Approval` - LEFT JOIN `users` AS `User` ON (`User`.`id`=`Approval`.`user_id`) - WHERE `Approval`.`created` >= FROM_UNIXTIME('{$startTime}') - AND `Approval`.`created` < FROM_UNIXTIME('{$endTime}') - GROUP BY yearmonth, `Approval`.`user_id`"; - - $results = $this->Approval->query($sql); - foreach ($results as $row) { - $label = strftime('%b %Y', strtotime($row['Approval']['created'])); - if (!in_array($label, $data['labels'])) { - $data['labels'][] = $label; - $data['teamcount'][] = 0; - $data['usercount'][] = 0; - } - $i = count($data['labels']) - 1; - $data['teamcount'][$i] += $row[0]['total']; - if ($row['User']['id'] == $userId) { - $data['usercount'][$i] += $row[0]['total']; - } - } - - return $data; - } - - - /** - * Generate data for category breakdown summary for user and team - * @param int $userId user to generate report for - * @param int $year year (default: current year) - * @param int $month month to summarize (default: generate data for entire year) - * @return array - */ - function _performanceSummaryByCategory($userId, $year=null, $month=null) { - $user = $this->User->findById($userId); - $data = array( - 'email' => $user['User']['email'], - 'firstname' => $user['User']['firstname'], - 'lastname' => $user['User']['lastname'], - 'labels' => array(), - 'usercount' => array(), - 'teamcount' => array()); - - $addonTypes = $this->Addontype->getNames(); - asort($addonTypes); - - $addonTypeKeys = array(); - foreach ($addonTypes as $key => $val) { - $addonTypeKeys[] = $key; - $data['labels'][] = $val; - $data['usercount'][] = 0; - $data['teamcount'][] = 0; - } - - // single month or year summary breakdown by category - // default date range is current year - if (is_null($year)) { - $year = date('Y'); - } - - if ($month > 0) { - $startTime = strtotime(sprintf('%d-%02d-01 00:00:00', $year, $month)); - $endTime = strtotime('+1 month', $startTime); - } else { - $startTime = strtotime(sprintf('%d-01-01 00:00:00', $year)); - $endTime = strtotime('+1 year', $startTime); - } - - $sql = "SELECT `Addon`.`addontype_id`, `User`.`id`, COUNT(*) AS `total` - FROM `approvals` AS `Approval` - LEFT JOIN `users` AS `User` ON (`User`.`id`=`Approval`.`user_id`) - LEFT JOIN `addons` AS `Addon` ON (`Addon`.`id`=`Approval`.`addon_id`) - WHERE `Approval`.`created` >= FROM_UNIXTIME('{$startTime}') - AND `Approval`.`created` < FROM_UNIXTIME('{$endTime}') - GROUP BY `Approval`.`user_id`, `Addon`.`addontype_id`"; - - $results = $this->Approval->query($sql); - foreach ($results as $row) { - $i = array_search($row['Addon']['addontype_id'], $addonTypeKeys); - if ($i === false) { - continue; // approvals should always have a known addontype ? - } - $data['teamcount'][$i] += $row[0]['total']; - if ($row['User']['id'] == $userId) { - $data['usercount'][$i] += $row[0]['total']; - } - } - - return $data; - } - - - /** - * AJAX Add-on and Author email lookup - */ - function addonAndAuthorLookup($queue='pending') { - if (!$this->SimpleAcl->actionAllowed('Admin', '%', $this->Session->read('User')) || - !$this->SimpleAcl->actionAllowed('Editor', '*', $this->Session->read('User')) ) { - $this->Amo->accessDenied(); - } - - $text = $_REQUEST['q']; - $this->Amo->clean($text, false); - $addonsAndEmails = array(); - - if (strlen($text) > 0 && in_array($queue, array('pending', 'nominated'))) { - // Use the base filter components to limit results to those in the queue - $sql_base = $this->Editors->baseQueueFilterQuery($queue); - - // search localized addon names - $sql = "SELECT DISTINCT IFNULL(`tr_l`.`localized_string`, `tr_en`.`localized_string`) AS `lname` - {$sql_base['FROM']} - {$sql_base['JOIN']} - LEFT JOIN `translations` AS `tr_l` ON - (`tr_l`.`id`=`Addon`.`name` AND `tr_l`.`locale`='".LANG."') - LEFT JOIN `translations` AS `tr_en` ON - (`tr_en`.`id`=`Addon`.`name` AND `tr_en`.`locale`=`Addon`.`defaultlocale`) - {$sql_base['WHERE']} - AND IFNULL(`tr_l`.`localized_string`, `tr_en`.`localized_string`) LIKE '%{$text}%' - ORDER BY `lname` ASC"; - - $results = $this->Version->query($sql); - if (!empty($results)) { - foreach ($results as $row) { - $addonsAndEmails[] = $row[0]['lname']; - } - } - - // search localized addon support emails - $emails = array(); - $sql = "SELECT IFNULL(`tr_l`.`localized_string`, `tr_en`.`localized_string`) AS `lemail` - {$sql_base['FROM']} - {$sql_base['JOIN']} - LEFT JOIN `translations` AS `tr_l` ON - (`tr_l`.`id`=`Addon`.`supportemail` AND `tr_l`.`locale`='".LANG."') - LEFT JOIN `translations` AS `tr_en` ON - (`tr_en`.`id`=`Addon`.`supportemail` AND `tr_en`.`locale`=`Addon`.`defaultlocale`) - {$sql_base['WHERE']} - AND IFNULL(`tr_l`.`localized_string`, `tr_en`.`localized_string`) LIKE '%{$text}%'"; - - $results = $this->Version->query($sql); - if (!empty($results)) { - foreach ($results as $row) { - $emails[] = $row[0]['lemail']; - } - } - - // search author emails - $sql = "SELECT `User`.`email` - {$sql_base['FROM']} - {$sql_base['JOIN']} - LEFT JOIN `addons_users` AS `au` ON (`Addon`.`id`=`au`.`addon_id`) - LEFT JOIN `users` AS `User` ON (`au`.`user_id`=`User`.`id`) - {$sql_base['WHERE']} - AND `au`.`role` IN(".AUTHOR_ROLE_ADMINOWNER."," - .AUTHOR_ROLE_ADMIN."," - .AUTHOR_ROLE_OWNER."," - .AUTHOR_ROLE_DEV.") - AND `User`.`email` LIKE '%{$text}%'"; - - $results = $this->Version->query($sql); - if (!empty($results)) { - foreach ($results as $row) { - $emails[] = $row['User']['email']; - } - } - - // sort, dedup, and merge - sort($emails); - $emails = array_unique($emails); - $addonsAndEmails = array_merge($addonsAndEmails, $emails); - } - - $this->set('addonsAndEmails', $addonsAndEmails); - $this->render('ajax/addon_and_author_lookup', 'ajax'); - } - - /** - * AJAX action for looking up appversions for the specified app - * @param int $app_id The application id - */ - function appversionLookup($app_id) { - $this->Amo->clean($app_id); - $results = $this->Appversion->findAllByApplicationId($app_id, - array('Appversion.id', 'Appversion.version'), 'Appversion.version DESC'); - $appversions = array(); - foreach ($results as $av) { - $appversions[] = $av['Appversion']; - } - - $this->publish('appversions', $appversions); - $this->render('ajax/appversion_lookup', 'ajax'); - } - - /** - * Count the number of (possibly filtered) items in the specified queue - * @param string $queue - * @param bool $useFilter (default=false) - * @return int - * @todo possibly cache results - */ - function _getCount($queue, $useFilter=false) { - $result = null; - - if ($useFilter && $this->Editors->buildQueueFilterQuery($queue)) { - $sql = $this->Editors->buildQueueFilterQuery($queue); - $result = $this->Addon->query( - "SELECT COUNT(*) {$sql['FROM']} {$sql['JOIN']} {$sql['WHERE']}"); - - } elseif ($queue == 'pending') { - $result = $this->File->query( - "SELECT COUNT(*) FROM `files` WHERE `status`=".STATUS_PENDING." GROUP BY `status`"); - - } elseif ($queue == 'nominated') { - $result = $this->Addon->query( - "SELECT COUNT(*) FROM `addons` WHERE `status`=".STATUS_NOMINATED." GROUP BY `status`"); - - } elseif ($queue == 'reviews') { - $result = $this->Review->query( - "SELECT COUNT(*) FROM `reviews` WHERE `editorreview`=1 GROUP BY `editorreview`"); - } - - $count = !empty($result) ? $result[0][0]['COUNT(*)'] : 0; - - return $count; - } - - /** - * Fetch an array of (possibly filtered) addons for the specified queue - * @param string $queue - * @return array - */ - function _buildQueue($queue) { - if (!in_array($queue, array('pending', 'nominated'))) { - return array(); - } - - $sql = $this->Editors->buildQueueFilterQuery($queue); - - // Setup pagination - $this->Pagination->total = $this->_getCount($queue, true); - - $_pagination_options = array(); - if ($queue == 'pending') { - if (!array_key_exists('show', $_GET) && $this->Session->read('editor_queue_pending_show')) { - $_pagination_options['show'] = $this->Session->read('editor_queue_pending_show'); - } else { - // If $_GET['show'] exists it overrides this in the pagination component - $_pagination_options['show'] = 50; - } - list($not_used,$_limit,$_page) = $this->Pagination->init(null, null, $_pagination_options); - $this->Session->write('editor_queue_pending_show', $_limit); - - } else { - if (!array_key_exists('show', $_GET) && $this->Session->read('editor_queue_nominated_show')) { - $_pagination_options['show'] = $this->Session->read('editor_queue_nominated_show'); - } else { - // If $_GET['show'] exists it overrides this in the pagination component - $_pagination_options['show'] = 50; - } - list($not_used,$_limit,$_page) = $this->Pagination->init(null,null,$_pagination_options); - $this->Session->write('editor_queue_nominated_show', $_limit); - } - - $_offset = ($_page > 0) ? $_limit * ($_page-1) : 0; // no negative offsets, thank you - - $extra_fields = ''; - if ($queue == 'pending') { - $extra_fields .= ", `File`.`id`, `File`.`platform_id`"; - } - - // Fetch the queue - $queue_sql = "SELECT - `Version`.`id`, - `Version`.`addon_id`, - `Version`.`version`, - `Version`.`created`, - `Version`.`modified` - {$extra_fields} - {$sql['FROM']} - {$sql['JOIN']} - {$sql['WHERE']} - {$sql['ORDER']} - LIMIT {$_limit} OFFSET {$_offset}"; - - $addons = $this->Version->query($queue_sql); - - //Merge in Addon details - if (!empty($addons)) { - foreach ($addons as $k => $addon) { - $addon = $this->Addon->findById($addon['Version']['addon_id'], - array('Addon.id', - 'Addon.name', 'Addon.defaultlocale', - 'Addon.addontype_id', 'Addon.prerelease', - 'Addon.sitespecific', 'Addon.externalsoftware', - 'Addon.created', 'Addon.nominationdate'), '', 0); - $addons[$k] = array_merge_recursive($addons[$k], $addon); - } - } - - $platforms = $this->Amo->getPlatformName(); - $applications = $this->Amo->getApplicationName(); - - //make modifications to the queue array - if (!empty($addons)) { - foreach ($addons as $k => $addon) { - //get min/max versions - if ($targetApps = $this->Amo->getMinMaxVersions($addon['Version']['id'])) { - foreach ($targetApps as $targetApp) { - $appName = $targetApp['translations']['localized_string']; - $addons[$k]['targetApps'][$appName]['min'] = $targetApp['min']['version']; - $addons[$k]['targetApps'][$appName]['max'] = $targetApp['max']['version']; - } - } - - //Age - if ($queue == 'pending') { - $age = time() - strtotime($addon['Version']['created']); - } - elseif ($queue == 'nominated') { - $age = time() - strtotime($addon['Addon']['created']); - $nominationage = time() - strtotime($addon['Addon']['nominationdate']); - $addons[$k]['nominationage'] = $this->_humanizeAge($nominationage); - } - - $addons[$k]['age'] = $this->_humanizeAge($age); - - //Generate any additional notes - $addons[$k]['notes'] = array(); - - //Platform-specific? - if (!empty($addon['Version'][0]['File'][0]['platform_id']) && $addon['Version'][0]['File'][0]['platform_id'] != 1) { - $os = array(); - foreach ($addon['Version'][0]['File'] as $file) { - $os[] = $platforms[$file['platform_id']]; - } - $addons[$k]['notes'][] = sprintf(_('editors_platform_x_only'), implode(', ', $os)); - } - elseif (!empty($addon['File']['platform_id']) && $addon['File']['platform_id'] != 1) { - $addons[$k]['notes'][] = sprintf(_('editors_platform_x_only'), $platforms[$addon['File']['platform_id']]); - } - - //Featured? - //@TODO - - //Site specific? - if ($addon['Addon']['sitespecific'] == 1) { - $addons[$k]['notes'][] = _('editors_site_specific'); - } - //Pre-release? - if ($addon['Addon']['prerelease'] == 1) { - $addons[$k]['notes'][] = _('editors_pre-release'); - } - //External software? - if ($addon['Addon']['externalsoftware'] == 1) { - $addons[$k]['notes'][] = _('editors_external_software'); - } - } - } - //pr($addons); - - return $addons; - } /** * Moderated Reviews Queue @@ -1448,87 +869,6 @@ class EditorsController extends AppController $this->set('page', 'reviewlog'); $this->render('reviewlog'); } - - /* Humanizes a Unix Timestamp */ - function _humanizeAge($age) { - $humanized = ''; - - //days - if ($age >= (60*60*24*2)) { - $humanized = sprintf(_('editors_x_days'), floor($age/(60*60*24))); - } - //1 day - elseif ($age >= (60*60*24)) { - $humanized = _('editors_one_day'); - } - //hours - elseif ($age >= (60*60*2)) { - $humanized = sprintf(_('editors_x_hours'), floor($age/(60*60))); - } - //hour - elseif ($age >= (60*60)) { - $humanized = _('editors_one_hour'); - } - //minutes - elseif ($age > 60) { - $humanized = sprintf(_('editors_x_minutes'), floor($age/60)); - } - //minute - else { - $humanized = _('editors_one_minute'); - } - - return $humanized; - } - - /* Generate a sortable key from the given timestamp in the form of 'YYYY-WW' */ - function _makeYearWeekKey($timestamp) { - $year = date('Y', $timestamp); - $week = date('W', $timestamp); - - // the end of December is often part of the first week for the following year - if ($week == '01' && date('m', $timestamp) == '12') { - $year++; - } - return "{$year}-{$week}"; - } - - /* Approximate team size at a point in time */ - function _teamSize($timestamp) { - $this->Amo->clean($timestamp); - - // Count the number of unique users that submitted a review during - // the 60 days leading up to the time specified. - $teamSize = 0; - $sql = "SELECT COUNT(DISTINCT `Approval`.`user_id`) AS `teamSize` - FROM `approvals` AS `Approval` - WHERE `Approval`.`created` < FROM_UNIXTIME('{$timestamp}') - AND `Approval`.`created` >= FROM_UNIXTIME('".strtotime('-60 day', $timestamp)."')"; - if ($results = $this->Approval->query($sql)) { - $teamSize = $results[0][0]['teamSize']; - } - return $teamSize; - } - - /* Fetch emails of all editors active in the last reviewDays days */ - function _recentEditors($reviewDays=90) { - $this->Amo->clean($reviewDays); - - $sql = "SELECT DISTINCT `User`.`email` - FROM `approvals` AS `Approval` - INNER JOIN `users` AS `User` ON (`User`.`id`=`Approval`.`user_id`) - WHERE `Approval`.`created` >= FROM_UNIXTIME('".strtotime("-{$reviewDays} day")."') - ORDER BY `User`.`email` ASC"; - - $editors = array(); - if ($results = $this->Approval->query($sql)) { - foreach ($results as $user) { - $editors[] = $user['User']['email']; - } - } - - return $editors; - } } ?> diff --git a/site/app/controllers/files_controller.php b/site/app/controllers/files_controller.php index 694085d..e662400 100644 --- a/site/app/controllers/files_controller.php +++ b/site/app/controllers/files_controller.php @@ -72,7 +72,7 @@ class FilesController extends AppController $this->File->id = $file_id; if (!$file = $this->File->read()) { - $this->flash(_('error_file_notfound'), '/'); + $this->flash($file_id, '/'); return; } $this->Addon->id = $file['Version']['addon_id']; @@ -85,7 +85,7 @@ class FilesController extends AppController } $addontype = $addon['Addon']['addontype_id']; - $startfile = 'install.rdf'; + $startfile = 'install.rdf2'; $path = REPO_PATH.'/'.$this->Addon->id.'/'.$file['File']['filename']; if (!file_exists($path)) { @@ -116,14 +116,12 @@ class FilesController extends AppController foreach ($contents as $content) { $isJar = false; - if ($addontype == ADDON_EXTENSION) { - $paths = split("/", $content['filename'], 2); - $pathinfo = pathinfo($path); - if ($pathinfo['extension'] == '.xo') - $startfile = $paths[0].'/activity/activity.info'; - else - $startfile = $paths[0].'/library/library.info'; - } + // we are sugar + $paths = split("/", $content['filename'], 2); + if ($addontype == ADDON_CONTENT) + $startfile = $paths[0].'/library/library.info'; + else + $startfile = $paths[0].'/activity/activity.info'; if (substr($content['filename'], strrpos($content['filename'], '.')) == '.jar') { $content['folder'] = 1; @@ -169,7 +167,7 @@ class FilesController extends AppController $contents = $this->_get_contents($path, $file, $addontype); if (is_bool($contents) && $contents == false) { - $this->flash(_('error_file_notfound'), '/'); + $this->flash($file, '/'); return; } $this->publish('filename', $file); diff --git a/site/app/controllers/images_controller.php b/site/app/controllers/images_controller.php index b25ec59..d20353d 100644 --- a/site/app/controllers/images_controller.php +++ b/site/app/controllers/images_controller.php @@ -22,7 +22,6 @@ * Contributor(s): * Andrei Hajdukewycz <sancus@off.net> (Original Author) * Justin Scott <fligtar@mozilla.com> - * Frederic Wenzel <fwenzel@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -40,7 +39,7 @@ class ImagesController extends AppController { var $name = 'Images'; - var $uses = array('Addon', 'Collection', 'Preview'); + var $uses = array('Addon', 'Preview'); var $components = array('Image'); var $autoRender = false; @@ -69,22 +68,6 @@ class ImagesController extends AppController } /** - * Renders the icon for the specified add-on - * @param int $addon_id ID of the add-on - * @param int $timestamp timestamp of last update (optional) - * @param bool $cache whether to memcache or not (optional) - */ - function collection_icon($collection_id, $timestamp = '', $cache = true) { - if ($cache !== true) { - $this->Collection->caching = false; - } - - if (!empty($collection_id)) { - $this->Image->renderCollectionIcon($collection_id); - } - } - - /** * Renders the full-size preview with the ID specified * @param int $preview_id ID of the preview to render * @param int $timestamp timestamp of last update (optional) diff --git a/site/app/controllers/pages_controller.php b/site/app/controllers/pages_controller.php index 2dd39d0..c22cd47 100644 --- a/site/app/controllers/pages_controller.php +++ b/site/app/controllers/pages_controller.php @@ -44,8 +44,8 @@ class PagesController extends AppController{ var $name = 'Pages'; var $helpers = array('Html', 'Localization'); - var $components = array('Image', 'Pagination'); - var $uses = array('Addon', 'Collection', 'File', 'Platform', 'User'); + var $components = array('Pagination'); + var $uses = array('User'); var $securityLevel = 'low'; @@ -109,21 +109,6 @@ class PagesController extends AppController{ $title = ___('page_title_submissionhelp', 'Submission Help'); break; case 'faq': $title = ___('page_title_faq', 'Frequently Asked Questions'); break; - case 'developer_faq': - $title = ___('page_title_developer_faq'); break; - case 'collector': - $title = ___('page_title_collector'); - $this->publish('collectionSearch', true); break; - case 'collector_faq': - $title = ___('page_title_collector_faq'); - $this->publish('collectionSearch', true); break; - case 'collector_features': - $title = ___('page_title_collector_features'); - $this->publish('collectionSearch', true); break; - case 'collector_firstrun': - $this->set('jsAdd', array('amo2009/collections')); - $title = ___('page_title_collector_firstrun'); - $this->publish('collectionSearch', true); break; default: if (!empty($path[$count - 1])) { $title = ucfirst($path[$count - 1]); diff --git a/site/app/controllers/reviews_controller.php b/site/app/controllers/reviews_controller.php index a62c557..f1dad56 100644 --- a/site/app/controllers/reviews_controller.php +++ b/site/app/controllers/reviews_controller.php @@ -48,7 +48,7 @@ class ReviewsController extends AppController var $components = array('Amo', 'Pagination', 'Session'); var $helpers = array('Html', 'Link', 'Localization', 'Pagination', 'Time'); var $namedArgs = true; - var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox', 'checkAdvancedSearch'); + var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox'); var $securityLevel = 'low'; @@ -552,9 +552,11 @@ class ReviewsController extends AppController // mark review for editor approval $this->Review->id = $reviewid; if (!$error && $this->Review->saveField('editorreview', 1)) { + // @partial translation fallback, 5/22/08 $this->publish('msg', ___('review_flag_success', 'Thanks; this review has been flagged for editor approval.')); } else { + // @partial translation fallback, 5/22/08 if (!isset($this->viewVars['msg'])) $this->publish('msg', ___('review_flag_error', 'Error flagging this review!')); diff --git a/site/app/controllers/search_controller.php b/site/app/controllers/search_controller.php index 670bc15..d2cba55 100644 --- a/site/app/controllers/search_controller.php +++ b/site/app/controllers/search_controller.php @@ -53,8 +53,8 @@ class SearchController extends AppController /** * Cake array for what tables this controller accesses */ - var $uses = array('Addon', 'Addontype', 'Collection', 'File', 'Translation', 'Platform', ); - var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox', 'checkAdvancedSearch'); + var $uses = array('Addon', 'Addontype', 'Translation', 'Platform', ); + var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox'); /** * Holds the sanitize component, used to clean variables in our custom queries @@ -66,7 +66,7 @@ class SearchController extends AppController var $helpers = array('Javascript', 'Pagination', 'Time'); // components to be used by this controller - var $components = array('Image', 'Pagination', 'Search', "Versioncompare", "Amo", "CollectionsListing"); + var $components = array('Image', 'Pagination', 'Search', "Versioncompare", "Amo"); // view layout var $layout = 'mozilla'; @@ -109,6 +109,15 @@ class SearchController extends AppController $_terms = ''; } $this->publish('search_terms', $_terms); + + if (!empty($this->params['url']['cat'])) { + $category = explode(',', $this->params['url']['cat']); + if (count($category) != 2 || !is_numeric($category[0]) || + !is_numeric($category[1])) + $category = array(0,0); + } else + $category = array(0,0); + $this->publish('category', $category); //if advanced search appid set, use it $appname = ""; @@ -121,21 +130,6 @@ class SearchController extends AppController } $this->publish('appid', APP_ID); //publish for element caching - // collection search is a special case - if (!empty($this->params['url']['cat']) && ($this->params['url']['cat'] == 'collections')) { - $this->_collections($_terms); - return; - } - - if (!empty($this->params['url']['cat'])) { - $category = explode(',', $this->params['url']['cat']); - if (count($category) != 2 || !is_numeric($category[0]) || - !is_numeric($category[1])) - $category = array(0,0); - } else - $category = array(0,0); - $this->publish('category', $category); - //if advanced search atype set, use it. $atype = -1; $addon_types = $this->Addontype->getNames(); @@ -166,8 +160,7 @@ class SearchController extends AppController $this->publish('sort', $sort); //publish for element caching //if advanced search hver and lver set (for version range), use the - $hver = 'any'; - $lver = -1; + $hver = $lver = -1; $vfuz = false; if (isset($this->params['url']['lver']) && isset( $this->params['url']['hver']) && isset( $this->params['url']['vfuz'])) { $hver = $this->params['url']['hver']; @@ -254,48 +247,6 @@ class SearchController extends AppController return $this->index(); // let index handle rss action } - - /** - * Search collections - */ - function _collections($terms = '') { - //search - $_result_ids = $this->Search->searchCollections($terms); - - //if advanced search pagination set, use it. - $pp = 10; - if (isset( $this->params['url']['pp']) && in_array($this->params['url']['pp'], $this->Pagination->resultsPerPage)) { - $pp = $this->params['url']['pp']; - } - $this->publish('pp', $pp); //publish for element caching - - // Fetch a sorted page of collections - $page_options = array('show' => $pp); - $collections = $this->CollectionsListing->fetchPage($_result_ids, $page_options); - - // Our view needs the current sort options - list($sort_opts, $sortby) = $this->CollectionsListing->sorting(); - - // Prep and render the view - $this->pageTitle = ___('search_collections_pagetitle', 'Collection Search Results').' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - - $this->publish('jsAdd', array('amo2009/collections', 'jquery-ui/jqModal.js')); - $this->publish('breadcrumbs', array( - sprintf(___('addons_home_pagetitle'), APP_PRETTYNAME) => '/', - ___('collections_breadcrumb') => '/collections' - )); - - $this->publish('collapse_categories', true); - $this->publish('collectionSearch', true); - $this->publish('query', $terms); - $this->publish('sort_opts', $sort_opts); - $this->publish('sortby', $sortby); - $this->publish('collections', $collections); - $this->forceCache(); - $this->render('collections'); - return; - } - } ?> diff --git a/site/app/controllers/statistics_controller.php b/site/app/controllers/statistics_controller.php index b6a7ffd..7acf45e 100644 --- a/site/app/controllers/statistics_controller.php +++ b/site/app/controllers/statistics_controller.php @@ -41,7 +41,7 @@ class StatisticsController extends AppController var $name = 'Statistics'; var $uses = array('Addon', 'Addontype', 'Application', 'User', 'Version'); var $components = array('Amo', 'Image', 'Stats'); - var $helpers = array('Html', 'Javascript', 'Listing', 'Localization', 'Statistics', 'Time'); + var $helpers = array('Html', 'Javascript', 'Listing', 'Localization', 'Time'); /** * Require login for all actions @@ -56,7 +56,7 @@ class StatisticsController extends AppController // Clean post data $this->Amo->clean($this->data); - $this->layout = 'amo2009'; + $this->layout = 'mozilla'; $this->pageTitle = _('statistics_pagetitle').' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); $this->cssAdd = array('stats/stats'); @@ -78,10 +78,6 @@ class StatisticsController extends AppController * Index */ function index($addon_id = 0) { - - // Get public stats for dashboard. - $this->publish('dailyStats',$this->Stats->getDailyStats()); - // If add-on id was specified, go to its statistics if (!empty($addon_id) || !empty($_GET['data']['Addon']['id'])) { if (!empty($addon_id)) @@ -94,11 +90,7 @@ class StatisticsController extends AppController // If not, figure out what to do else { $session = $this->Session->read('User'); - if ($session) { - $addons = $this->Addon->getAddonsByUser($session['id']); - } else { - $addons = array(); - } + $addons = $this->Addon->getAddonsByUser($session['id']); $this->publish('addons', $addons); // If user can access all add-on stats, pull all @@ -148,6 +140,7 @@ class StatisticsController extends AppController return; } + $this->publish('suppressJQuery', true); $this->jsAdd = array( //'simile/timeplot/timeplot-api.js?local', 'jquery-compressed.js', @@ -218,23 +211,6 @@ class StatisticsController extends AppController $stats['last_downloads'] = $statsQry[0]['download_counts']['count']; $stats['last_downloads_date'] = $statsQry[0]['download_counts']['date']; } - // Grouping by week is not quite what we want, since the current - // week is going to be shorter than (the complete) last week. - // If you know how to get (now, now - 7), (now - 8, now - 14) ranges - // in a group by, feel free to improve this. - $thisWeek = $this->Addon->query("SELECT AVG(count) as `count` FROM update_counts - WHERE addon_id={$addon_id} - AND date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)"); - $prevWeek = $this->Addon->query("SELECT AVG(count) as `count` FROM update_counts - WHERE addon_id={$addon_id} - AND date <= DATE_SUB(CURDATE(), INTERVAL 7 DAY) - AND date >= DATE_SUB(CURDATE(), INTERVAL 14 DAY)"); - if ($thisWeek && $prevWeek) { - $thisWeek = $thisWeek[0][0]['count']; - $prevWeek = $prevWeek[0][0]['count']; - $stats['weekly_updatepings'] = $thisWeek; - $stats['weekly_updatepings_change'] = $prevWeek > 0 ? (($thisWeek - $prevWeek) / $prevWeek) * 100 : 0; - } } $this->set('stats', $stats); $this->pageTitle = $addon['Translation']['name']['string'].' :: '._('statistics_pagetitle').' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); @@ -276,7 +252,7 @@ class StatisticsController extends AppController 'statistics_js_plotselection_options_csv_tooltip' => _('statistics_js_plotselection_options_csv_tooltip') )); - $this->render('addon', 'amo2009'); + $this->render('addon', 'mozilla'); } else { $this->publish('rss_title', sprintf(_('statistics_title_addon_stats'), $addon['Translation']['name']['string'])); diff --git a/site/app/controllers/tests_controller.php b/site/app/controllers/tests_controller.php index 05893ca..a9a9c89 100644 --- a/site/app/controllers/tests_controller.php +++ b/site/app/controllers/tests_controller.php @@ -37,7 +37,6 @@ */ vendor('simpletest/reporter'); vendor('simpletest'.DS.'unit_tester', 'simpletest'.DS.'web_tester', 'simpletest'.DS.'mock_objects', 'simpletest'.DS.'xml', 'simpletest'.DS.'remote'); -vendor('phpQuery/phpQuery'); require_once(TESTS.'test_manager.php'); require_once(TESTS.'groups.php'); require_once(TESTS.'cake_reporter.php'); diff --git a/site/app/controllers/users_controller.php b/site/app/controllers/users_controller.php index a848e4e..485571c 100644 --- a/site/app/controllers/users_controller.php +++ b/site/app/controllers/users_controller.php @@ -41,12 +41,12 @@ class UsersController extends AppController { var $name = 'Users'; - var $uses = array('User', 'Addon', 'Collection', 'Eventlog', 'Review', 'Version'); + var $uses = array('User', 'Addon', 'Eventlog', 'Review', 'Version'); var $components = array('Amo', 'Email', 'Ldap', 'Session', 'Pagination', 'Recaptcha'); var $helpers = array('Html', 'Link', 'Javascript'); - var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox', 'checkAdvancedSearch'); + var $beforeFilter = array('checkCSRF', 'getNamedArgs', '_checkSandbox'); var $exceptionCSRF = array("/users/login", "/users/register", "/users/pwreset"); - var $layout = 'amo2009'; + var $layout = 'mozilla'; var $namedArgs = true; var $securityLevel = 'high'; @@ -110,7 +110,7 @@ class UsersController extends AppController $this->Amo->clean($this->data); $this->User->data = $this->data; // hash password(s) - $this->User->data['User']['password'] = $this->User->createPassword($this->User->data['User']['password']); + $this->User->data['User']['password'] = md5($this->User->data['User']['password']); // compare passwords if ($this->data['User']['password'] !== $this->data['User']['confirmpw']) @@ -238,8 +238,7 @@ class UsersController extends AppController } else { // user found: send pw reset URL via email $this->publish('data', $thisuser); - $resetCode = $this->User->setResetCode($thisuser['User']['id']); - $this->publish('resetcode', $resetCode); + $this->publish('resetcode', md5($thisuser['User']['password'])); $this->Email->template = 'email/pwreset'; $this->Email->to = $this->data['User']['email']; $this->Email->subject = sprintf(_('user_email_pwreset_subject'), APP_PRETTYNAME); @@ -250,16 +249,7 @@ class UsersController extends AppController } return; } - - // id and/or code was sent, make sure the page isn't cached. - $this->disableCache(); - - // Remove 'id/resetcode' from the URI so it doesn't get echoed. - $_SERVER['REQUEST_URI'] = preg_replace('@pwreset/.*$@', 'pwreset/', $_SERVER['REQUEST_URI']); - if (isset($this->params['url']['url'])) { - $this->params['url']['url'] = preg_replace('@pwreset/.*$@', 'pwreset/', $this->params['url']['url']); - } - + if (!$id || !$code) { $this->flash(sprintf(_('error_missing_argument'), 'user_id or code'), '/', 3); return; @@ -271,8 +261,7 @@ class UsersController extends AppController return; } - if (!$this->User->checkResetCode($id, $code)) { - // TODO: update message re: expiration + if ($code !== md5($thisuser['User']['password'])) { $this->flash(_('error_user_badconfirmationcode'), '/', 3); return; } @@ -297,7 +286,7 @@ class UsersController extends AppController // store new pw $newpw = array(); - $newpw['User']['password'] = $this->User->createPassword($this->data['User']['password']); + $newpw['User']['password'] = md5($this->data['User']['password']); $this->User->id = $id; $this->User->save($newpw); // success @@ -356,7 +345,7 @@ class UsersController extends AppController return; } - if ($this->User->checkPassword($someone['User'], $this->data['Login']['password'])) { + if (!empty($someone['User']['password']) && $someone['User']['password'] == md5($this->data['Login']['password'])) { //Set expiration to two weeks if they check 'remember me' $expirationTime = isset($this->data['Login']['remember']) ? 60*60*24*7*2 : 0; @@ -414,10 +403,7 @@ class UsersController extends AppController $this->pageTitle = _('users_edit_pagetitle').' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); $this->publish('cssAdd', array('forms', 'jquery-ui/flora/flora.tabs')); $this->publish('jsAdd', array('jquery-ui/ui.core.min', 'jquery-ui/ui.tabs.min')); - - $translations = $this->User->getAllTranslations($sessionuser['id']); - $this->set('translations', $translations); - + if (empty($this->data)) { $this->publish('userAddons', $this->Addon->getAddonsByUser($sessionuser['id'])); @@ -434,14 +420,12 @@ class UsersController extends AppController $changed['nickname'] = $this->data['User']['nickname']; $changed['emailhidden'] = $this->data['User']['emailhidden']; $changed['homepage'] = $this->data['User']['homepage']; - $changed['display_collections'] = $this->data['User']['display_collections']; - $changed['display_collections_fav'] = $this->data['User']['display_collections_fav']; if (!empty($this->data['User']['password']) && !empty($this->data['User']['newpassword'])) { // trying to change the password - if (!$this->User->checkPassword($sessionuser, $this->data['User']['password'])) + if ($sessionuser['password'] !== md5($this->data['User']['password'])) $this->User->invalidate('password'); if ($this->data['User']['newpassword'] != $this->data['User']['confirmpw']) $this->User->invalidate('confirmpw'); @@ -449,7 +433,7 @@ class UsersController extends AppController // store the new chosen pw to the "edited" array. // If we invalidated fields up here, it's not going to be // stored anyway. - $changed['password'] = $this->User->createPassword($this->data['User']['newpassword']); + $changed['password'] = md5($this->data['User']['newpassword']); } // nickname has to be unique @@ -511,11 +495,6 @@ class UsersController extends AppController } // if we get here, the data was saved successfully - // save author "about me" - list($localizedFields, $unlocalizedFields) = $this->User->splitLocalizedFields($this->data['User']); - $this->Amo->clean($localizedFields); - $this->User->saveTranslations($sessionuser['id'], $this->params['form']['data']['User'], $localizedFields); - // send out confirmation email if necessary if ($newemail !== false) { $this->set('newemail', $newemail); @@ -531,7 +510,11 @@ class UsersController extends AppController // send out the confirmation email $this->Email->template = 'email/emailchange'; $this->Email->to = $newemail; - $this->Email->subject = sprintf(___('user_emailchange_subject'), APP_PRETTYNAME); + // @partial translation fallback, 5/6/08 + if (_('user_emailchange_subject') != 'user_emailchange_subject') + $this->Email->subject = sprintf(_('user_emailchange_subject'), APP_PRETTYNAME); + else + $this->Email->subject = sprintf('Please confirm your email address change at %1$s Add-ons', APP_PRETTYNAME); $result = $this->Email->send(); } @@ -594,7 +577,13 @@ class UsersController extends AppController // is the token expired (48 hours max)? if (time()-$changedata[2] > 48*60*60) { - $this->publish('errormsg', ___('error_user_emailchange_expired')); + // @partial translation fallback, 5/6/08 + if (_('error_user_emailchange_expired') != 'error_user_emailchange_expired') + $this->publish('errormsg', _('error_user_emailchange_expired')); + else + $this->publish('errormsg', 'The email change has expired. Please change ' + .'your email address again in your user profile and click the link ' + .'in the confirmation email as soon as you receive it.'); $this->render(); return; } @@ -687,20 +676,6 @@ class UsersController extends AppController } $this->publish('reviews', $reviews); - // get user's own and favorite collections, if they allowed that - if ($thisuser['User']['display_collections']) { - $coll_ids = $this->Collection->getCollectionsByUser($thisuser['User']['id']); - $coll = $this->Collection->findAll(array('Collection.id'=>$coll_ids, 'listed'=>1), - array('name', 'description', 'uuid', 'nickname', 'application_id'), 'Translation.name'); - $this->publish('coll', $coll); - } - if ($thisuser['User']['display_collections_fav']) { - $coll_ids = $this->Collection->getSubscriptionsByUser($thisuser['User']['id']); - $coll_fav = $this->Collection->findAll(array('Collection.id'=>$coll_ids, 'listed'=>1), - array('name', 'description', 'uuid', 'nickname', 'application_id'), 'Translation.name'); - $this->publish('coll_fav', $coll_fav); - } - if (!empty($thisuser['User']['nickname'])) $name = $thisuser['User']['nickname']; else @@ -738,7 +713,7 @@ class UsersController extends AppController } // password entered correctly? - if (!$this->User->checkPassword($deluser, $this->data['User']['password'])) { + if (md5($this->data['User']['password']) != $deluser['password']) { $this->set('deleteerror', 'password'); $this->data['User']['password'] = ''; // do not post back password return; diff --git a/site/app/controllers/versions_controller.php b/site/app/controllers/versions_controller.php index 9e7cbdc..ee52d96 100644 --- a/site/app/controllers/versions_controller.php +++ b/site/app/controllers/versions_controller.php @@ -39,30 +39,14 @@ class VersionsController extends AppController { var $name = 'Versions'; - var $uses = array('Addon', 'License', 'Translation', 'Version'); - var $components = array('Amo', 'Pagination'); + var $uses = array('Translation', 'Version'); function beforeFilter() { // Disable ACLs because this controller is entirely public. $this->SimpleAuth->enabled = false; $this->SimpleAcl->enabled = false; } - - function license($version_id) { - $version = $this->Version->findById($version_id); - $addon = $this->Addon->getAddon($version['Version']['addon_id']); - $license_text = $this->License->getText($version['Version']['license_id']); - $this->set('version', $version); - $this->publish('addon', $addon); - $this->publish('license_text', $license_text); - - // set up view, then render - $this->layout = 'amo2009'; - $this->pageTitle = sprintf(___('versions_license_header_source'), - $addon['Translation']['name']['string'], $version['Version']['version']) - .' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); - } - + /** * Displays a version's releasenotes in the form requested for updateInfoURLs * See http://developer.mozilla.org/en/docs/Extension_Versioning%2C_Update_and_Compatibility @@ -81,15 +65,15 @@ class VersionsController extends AppController $this->Version->setLang($locale, $this); } } - + $version = $this->Version->findById($version_id, array('Version.releasenotes'), null, -1); - + $updateInfo = $version[0]['releasenotes']; - + $this->publish('updateInfo', $updateInfo); $this->render('update_info', 'ajax'); } } -?> +?>
\ No newline at end of file |