소스 검색

! New profile menu goodness

Signed-off-by: Peter Spicer <[email protected]>
Peter Spicer 10 년 전
부모
커밋
7c4bdc5621

+ 83 - 0
Sources/Profile.php

@@ -98,6 +98,14 @@ function ModifyProfile($post_errors = array())
 						'any' => 'profile_view',
 					),
 				),
+				'popup' => array(
+					'function' => 'profile_popup',
+					'permission' => array(
+						'own' => 'is_not_guest',
+						'any' => array(),
+					),
+					'select' => 'summary',
+				),
 				'statistics' => array(
 					'label' => $txt['statPanel'],
 					'file' => 'Profile-View.php',
@@ -689,6 +697,81 @@ function ModifyProfile($post_errors = array())
 		$context['page_title'] = $txt['profile'] . (isset($txt[$current_area]) ? ' - ' . $txt[$current_area] : '');
 }
 
+/**
+ * Set up the requirements for the profile popup - the area that is shown as the popup menu for the current user.
+ *
+ * @param int $memID
+ */
+function profile_popup($memID)
+{
+	global $context, $scripturl, $txt;
+
+	// We only want to output our little layer here.
+	$context['template_layers'] = array();
+
+	// This list will pull from the master list wherever possible. Hopefully it should be clear what does what.
+	$profile_items = array(
+		array(
+			'menu' => 'info',
+			'area' => 'summary',
+			'title' => $txt['popup_summary'],
+		),
+		array(
+			'menu' => 'edit_profile',
+			'area' => 'account',
+		),
+		array(
+			'menu' => 'info',
+			'area' => 'showposts',
+			'title' => $txt['popup_showposts'],
+		),
+		array(
+			'menu' => 'edit_profile',
+			'area' => 'forumprofile',
+		),
+		array(
+			'menu' => 'edit_profile',
+			'area' => 'notification',
+		),
+		array(
+			'menu' => 'edit_profile',
+			'area' => 'theme',
+			'title' => $txt['popup_preferences'],
+		),
+		array(
+			'menu' => 'edit_profile',
+			'area' => 'ignoreboards',
+		),
+		array(
+			'menu' => 'edit_profile',
+			'area' => 'lists',
+			'url' => $scripturl . '?action=profile;area=lists;sa=ignore',
+			'title' => $txt['popup_ignore'],
+		),
+		array(
+			'menu' => 'edit_profile',
+			'area' => 'groupmembership',
+		),
+		array(
+			'menu' => 'profile_action',
+			'area' => 'subscriptions',
+		),
+	);
+
+	call_integration_hook('integrate_profile_popup', array(&$profile_items));
+
+	// Now check if these items are available
+	$context['profile_items'] = array();
+	$menu_context = &$context[$context['profile_menu_name']]['sections'];
+	foreach ($profile_items as $item)
+	{
+		if (isset($menu_context[$item['menu']]['areas'][$item['area']]))
+		{
+			$context['profile_items'][] = $item;
+		}
+	}
+}
+
 /**
  * Load any custom fields for this area... no area means load all, 'summary' loads all public ones.
  *

+ 11 - 25
Sources/Subs.php

@@ -3798,32 +3798,12 @@ function setupMenuContext()
 
 	$cacheTime = $modSettings['lastActive'] * 60;
 
-	// This is for showing the nice profile menu up top. Sub-menus are not supported.
-	$profile_menu = array(
-		'account' => array(
-			'title' => $txt['account'],
-			'href' => $scripturl . '?action=profile;area=account',
-			'show' => allowedTo(array('profile_forum_any', 'profile_forum_own')),
-		),
-		'profile' => array(
-			'title' => $txt['forumprofile'],
-			'href' => $scripturl . '?action=profile;area=forumprofile',
-			'show' => allowedTo(array('profile_forum_any', 'profile_forum_own')),
-		),
-		'theme' => array(
-			'title' => $txt['theme'],
-			'href' => $scripturl . '?action=profile;area=theme',
-			'show' => allowedTo(array('profile_extra_any', 'profile_extra_own', 'profile_extra_any')),
-		),
-	);
-	call_integration_hook('integrate_profile_buttons', array(&$profile_menu));
-	foreach ($profile_menu as $item => $details)
+	// There is some menu stuff we need to do if we're coming at this from a non-guest perspective.
+	if (!$context['user']['is_guest'])
 	{
-		if ((isset($details['enabled']) && empty($details['enabled'])) || empty($details['show']))
-			unset ($profile_menu[$item]);
-		// OK, so the item's good. Let's push this into $context but save a little memory as we do.
-		unset ($details['show']);
-		$context['profile_menu'][$item] = $details;
+		addInlineJavascript('
+	var user_menus = new smc_PopupMenu();
+	user_menus.add("profile", "' . $scripturl . '?action=profile;area=popup");', true);
 	}
 
 	// All the buttons we can possible want and then some, try pulling the final list of buttons from cache first.
@@ -4062,6 +4042,12 @@ function setupMenuContext()
 	elseif ($context['current_action'] == 'groups' && $context['allow_moderation_center'])
 		$current_action = 'moderate';
 
+	// There are certain exceptions to the above where we don't want anything on the menu highlighted.
+	if ($context['current_action'] == 'profile' && !empty($context['user']['is_owner']))
+	{
+		$current_action = 'self_profile';
+		$context['self_profile'] = true;
+	}
 	// Not all actions are simple.
 	if (!empty($needs_action_hook))
 		call_integration_hook('integrate_current_action', array(&$current_action));

+ 45 - 0
Themes/default/Profile.template.php

@@ -42,6 +42,51 @@ function template_profile_below()
 {
 }
 
+// Tempate for showing off the spiffy popup of the menu
+function template_profile_popup()
+{
+	global $context, $scripturl;
+
+	// Unlike almost every other template, this is designed to be included into the HTML directly via $().load()
+
+	echo '
+		<div class="profile_user_info">';
+
+	// Firstly, an avatar.
+	if (!empty($context['user']['avatar']))
+		echo '
+			<a href="', $scripturl, '?action=profile" class="avatar">', $context['user']['avatar']['image'], '</a>';
+
+	// Then a couple of bits and pieces that might be interesting
+	echo '
+			<ol>
+				<li class="profile_username"><a href="', $scripturl, '?action=profile">', $context['user']['name'], '</a></li>
+				<li class="profile_group">', $context['member']['group'], '</li>
+			</ol>
+			<br class="clear" />';
+
+	echo '
+		</div>
+		<div class="profile_user_links">
+			<ol>';
+
+	$menu_context = &$context[$context['profile_menu_name']];
+	foreach ($context['profile_items'] as $item)
+	{
+		$area = &$menu_context['sections'][$item['menu']]['areas'][$item['area']];
+		$item_url = (isset($item['url']) ? $item['url'] : (isset($area['url']) ? $area['url'] : $menu_context['base_url'] . ';area=' . $item['area'])) . $menu_context['extra_parameters'];
+		echo '
+				<li>
+					', $area['icon'], '<a href="', $item_url, '">', !empty($item['title']) ? $item['title'] : $area['label'], '</a>
+				</li>';
+	}
+
+	echo '
+			</ol>
+			<br class="clear" />
+		</div>';
+}
+
 // This template displays users details without any option to edit them.
 function template_summary()
 {

+ 46 - 23
Themes/default/css/index.css

@@ -1050,7 +1050,7 @@ img.sort, .sort {
 	float: left;
 	width: 100%;
 }
-.dropmenu {
+.dropmenu, #top_info {
 	float: left;
 	width: 100%;
 	position: relative;
@@ -1060,7 +1060,7 @@ img.sort, .sort {
 	padding: 0 40px 0 0;
 }
 /* Level 1 button background. */
-.dropmenu li {
+.dropmenu li, #top_info > li {
 	margin: 0 2px 0 2px;
 	padding: 0 0 0.3em 0;
 	float: left;
@@ -1069,18 +1069,19 @@ img.sort, .sort {
 	position: relative;
 }
 /* For cases where we want to spotlight something specific to an item, e.g. an amount */
-.dropmenu li .amt
-{
+.dropmenu li .amt, #top_info li .amt {
 	padding: 0 4px;
 	color: white;
 	background: #6d90ad;
 	border-radius: 4px;
 }
-.dropmenu li .active .amt
-{
+.dropmenu li .active .amt, #top_info li .active .amt {
 	background: none;
 	color: inherit;
 }
+#top_info .top_menu.visible {
+	margin-left: 9999px;
+}
 /* Needed for new PM notifications. */
 .dropmenu li strong {
 	color: #333;
@@ -1091,7 +1092,7 @@ img.sort, .sort {
 	top: 0;
 	right: 0;
 }
-.dropmenu li a {
+.dropmenu li a, #top_info > li > a {
 	padding: 0 7px 0 7px;
 	margin: 0 0 0 0;
 	display: block;
@@ -1099,7 +1100,7 @@ img.sort, .sort {
 	border-radius: 4px;
 }
 /* Level 1 active button. */
-.dropmenu li a.active {
+.dropmenu li a.active, #top_info li a.active {
 	background: orange;
 	color: #fff;
 	font-weight: bold;
@@ -1108,7 +1109,7 @@ img.sort, .sort {
 	text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6);
 }
 /* Level 1 hover effects. */
-.dropmenu li a:hover, .dropmenu li:hover a, .dropmenu li a:focus {
+.dropmenu li a:hover, .dropmenu li:hover a, .dropmenu li a:focus, #top_info > li > a:hover, #top_info > li:hover > a, #top_info > li > a.open {
 	background: #597b9f;
 	border: 1px solid #4a6b8c;
 	color: #fff;
@@ -1125,6 +1126,36 @@ img.sort, .sort {
 	box-shadow: 0 5px 5px rgba(255,255,255,0.2) inset;
 	text-shadow: none;
 }
+/* User information. */
+#profile_menu a.avatar {
+	float: left;
+	margin: 0 20px 0 10px;
+}
+#profile_menu a.avatar img {
+	height: 100%;
+	width: 100%;
+	max-width: 50px;
+	max-height: 50px;
+}
+#profile_menu .profile_user_info ol {
+	float: left;
+	max-width: 70%;
+}
+#profile_menu .profile_username {
+	max-width: 95%;
+	overflow: hidden;
+	font-size: 150%;
+}
+#profile_menu .profile_user_links ol {
+	margin: 10px;
+}
+#profile_menu .profile_user_links li {
+	float: left;
+	width: 50%;
+}
+#profile_menu .profile_user_links li img {
+	margin-bottom: -1px;
+}
 /* This CSS is for adding top level subsection indicators, just in case anyone wants them. */
 /* I'm not that keen on them, but perhaps the commented code should be left as a handy demo. */
 /*
@@ -1145,7 +1176,7 @@ img.sort, .sort {
 }
 */
 /* Levels 2 and 3 submenu wrapper. */
-.dropmenu li ul {
+.dropmenu li ul, .top_menu {
 	z-index: 90;
 	position: absolute;
 	top: 2.25em;
@@ -1183,6 +1214,10 @@ img.sort, .sort {
 	vertical-align: middle;
 	margin: 0 0 0 -4px;
 }
+/* The profile/pm menus are declared off .dropmenu li ul for consistency but have other characteristics. */
+.top_menu {
+	width: 25em;
+}
 /* Note: The next declarations are for keyboard access with js disabled. */
 .dropmenu ul a:focus, .dropmenu ul ul a:focus {
 	margin-left: 9990px;
@@ -1372,7 +1407,7 @@ img.sort, .sort {
 	margin: 0;
 	padding: 5px 9px 4px 9px;
 	line-height: 1.3em;
-	width: 50%;
+	width: auto;
 }
 /*#top_section ul li {
 	margin-bottom: 2px;
@@ -1498,18 +1533,6 @@ ul li.greeting {
 	overflow: hidden;
 	font-size: 0.9em;
 }
-/* User information. */
-#inner_wrap a.avatar {
-	float: left;
-	margin: 0 10px 5px 0;
-}
-/* No more huge avatars up top. YAY! */
-#inner_wrap a.avatar img {
-	height: 100%;
-	width: 100%;
-	max-width: 40px;
-	max-height: 40px;
-}
 /* News section. */
 #inner_wrap .news {
 	max-width: 50%;

+ 7 - 28
Themes/default/index.template.php

@@ -184,23 +184,11 @@ function template_body_above()
 	if ($context['user']['is_logged'])
 	{
 		// Firstly, the user's menu
-		$is_current_user = $context['current_action'] == 'profile' && !empty($context['user']['is_owner']);
 		echo '
-			<ul class="floatleft dropmenu" id="top_info">
+			<ul class="floatleft" id="top_info">
 				<li>
-					<a href="', $scripturl, '?action=profile"', $is_current_user ? ' class="active"' : '', '>', $context['user']['name'], !empty($context['profile_menu']) ? ' &#9660;' : '', '</a>';
-		if (!empty($context['profile_menu']))
-		{
-			echo '
-					<ul>';
-			foreach ($context['profile_menu'] as $key => $item)
-				echo '
-						<li>', !empty($item['href']) ? '<a href="' . $item['href'] . '">' . $item['title'] . '</a>' : $item['title'], '</li>';
-			echo '
-					</ul>';
-		}
-
-		echo '
+					<a href="', $scripturl, '?action=profile"', !empty($context['self_profile']) ? ' class="active"' : '', ' id="profile_menu_top" onclick="return false;">', $context['user']['name'], ' &#9660;</a>
+					<div id="profile_menu" class="top_menu"></div>
 				</li>';
 
 		// Are there any members waiting for approval?
@@ -219,7 +207,7 @@ function template_body_above()
 	// Otherwise they're a guest. Ask them to either register or login.
 	else
 		echo '
-			<ul class="floatleft" id="top_info">
+			<ul class="floatleft">
 				<li>', sprintf($txt[$context['can_register'] ? 'welcome_guest_register' : 'welcome_guest'], $txt['guest_title'], $scripturl . '?action=login'), '</li>
 			</ul>';
 
@@ -288,7 +276,7 @@ function template_body_above()
 		<div id="upper_section">
 			<div id="inner_section">
 				<div id="inner_wrap">
-					<div class="user floatright">';
+					<div class="user">';
 
 	// Otherwise they're a guest - this time ask them to either register or login - lazy bums...
 	if (!empty($context['show_login_bar']))
@@ -318,19 +306,10 @@ function template_body_above()
 							<input type="hidden" name="', $context['login_token_var'], '" value="', $context['login_token'], '" />
 						</form>';
 	}
-
-		// If the user is logged in, display stuff like their name, new messages, etc.
-	if ($context['user']['is_logged'])
+	else
 	{
-		if (!empty($context['user']['avatar']))
-			echo '
-						<a href="', $scripturl, '?action=profile" class="avatar">', $context['user']['avatar']['image'], '</a>';
-
 		echo '
-						<ul>
-							<li class="greeting">', $txt['hello_member_ndt'], ' <span>', $context['user']['name'], '</span></li>
-							<li>', $context['current_time'], '</li>
-						</ul>';
+						', $context['current_time'];
 	}
 
 	echo'

+ 6 - 0
Themes/default/languages/Profile.english.php

@@ -3,6 +3,12 @@
 
 global $scripturl, $context;
 
+// Some of the things from the popup need their own descriptions
+$txt['popup_summary'] = 'My Profile';
+$txt['popup_showposts'] = 'My Posts';
+$txt['popup_ignore'] = 'Ignore People';
+$txt['popup_preferences'] = 'Preferences';
+
 $txt['no_profile_edit'] = 'You are not allowed to change this person\'s profile.';
 $txt['website_title'] = 'Website title';
 $txt['website_url'] = 'Website URL';

+ 71 - 0
Themes/default/scripts/script.js

@@ -337,6 +337,77 @@ function reqOverlayDiv(desktopURL, sHeader, sIcon)
 	return false;
 }
 
+// Create the popup menus for the top level/user menu area.
+function smc_PopupMenu(oOptions)
+{
+	this.opt = (typeof oOptions == 'object') ? oOptions : {};
+	this.opt.menus = {};
+}
+
+smc_PopupMenu.prototype.add = function (sItem, sUrl)
+{
+	var $menu = $('#' + sItem + '_menu'), $item = $('#' + sItem + '_menu_top');
+	if ($item.length == 0)
+		return;
+
+	this.opt.menus[sItem] = {open: false, loaded: false, sUrl: sUrl, itemObj: $item, menuObj: $menu };
+
+	$item.click({obj: this}, function (e) {
+		e.preventDefault();
+		if (e.target != this)
+			return;
+			
+		e.data.obj.toggle(sItem);
+	});
+}
+
+smc_PopupMenu.prototype.toggle = function (sItem)
+{
+	if (!!this.opt.menus[sItem].open)
+		this.close(sItem);
+	else
+		this.open(sItem);
+}
+
+smc_PopupMenu.prototype.open = function (sItem)
+{
+	this.closeAll();
+
+	if (!this.opt.menus[sItem].loaded)
+	{
+		this.opt.menus[sItem].menuObj.html('<div class="loading">' + (typeof(ajax_notification_text) != null ? ajax_notification_text : '') + '</div>');
+		this.opt.menus[sItem].menuObj.load(this.opt.menus[sItem].sUrl);
+		this.opt.menus[sItem].loaded = true;
+	}
+
+	this.opt.menus[sItem].menuObj.addClass('visible');
+	this.opt.menus[sItem].itemObj.addClass('open');
+	this.opt.menus[sItem].open = true;
+
+	// Now set up closing the menu if we click off.
+	$(document).on('click.menu', {obj: this}, function(e) {
+		if ($(e.target).closest('#top_info').length)
+			return;
+		e.data.obj.closeAll();
+		$(document).off('click.menu');
+	});
+}
+
+smc_PopupMenu.prototype.close = function (sItem)
+{
+	this.opt.menus[sItem].menuObj.removeClass('visible');
+	this.opt.menus[sItem].itemObj.removeClass('open');
+	this.opt.menus[sItem].open = false;
+	$(document).off('click.menu');
+}
+
+smc_PopupMenu.prototype.closeAll = function ()
+{
+	for (var prop in this.opt.menus)
+		if (!!this.opt.menus[prop].open)
+			this.close(prop);
+}
+
 // *** smc_Popup class.
 function smc_Popup(oOptions)
 {