diff options
Diffstat (limited to 'plugins/jetpack/modules/masterbar/admin-menu')
16 files changed, 4398 insertions, 0 deletions
diff --git a/plugins/jetpack/modules/masterbar/admin-menu/admin-menu-rtl.css b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu-rtl.css new file mode 100644 index 00000000..854a83b1 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu-rtl.css @@ -0,0 +1,490 @@ +/* Do not modify this file directly. It is concatenated from individual module CSS files. */ +#adminmenu { + margin: 0; +} + +/** + * Menu width + */ +#wpcontent, +#wpfooter { + margin-right: 272px; +} + +#adminmenuback, +#adminmenuwrap, +#adminmenu, +#adminmenu .wp-submenu { + width: 272px; +} + +#adminmenu .wp-submenu { + right: 272px; +} + +#adminmenu .wp-not-current-submenu .wp-submenu, +.folded #adminmenu .wp-has-current-submenu .wp-submenu { + min-width: 272px; +} + +/** + * Fixes Gutenberg in not fullscreen mode. + */ + @media (min-width: 783px) { + .interface-interface-skeleton, + .edit-post-layout .components-editor-notices__snackbar { + right: 272px; + } +} + +@media (min-width: 961px) { + .auto-fold .interface-interface-skeleton, + .auto-fold .edit-post-layout .components-editor-notices__snackbar, + .jp-dialogue-modern-full__container { + right: 272px; + } + + .global-notices { + max-width: calc( 100% - 48px - 272px ); + } +} + +/** + * Jetpack logo + */ +#adminmenu [class*="activity-log"] .wp-menu-image img { + padding-top: 7px; +} + +/** + * Site Card + */ +#adminmenu .toplevel_page_site-card .wp-menu-name { + margin-right: 40px; /* icon width (32) + padding (8). */ + padding: 0; +} + +#adminmenu li.toplevel_page_site-card a { + padding: 10px 8px 10px 0; +} + +/** + * Site Notices + */ +#adminmenu a.toplevel_page_site-notices:hover, +#adminmenu a.toplevel_page_site-notices:focus, +#adminmenu li.toplevel_page_site-notices:hover, +#adminmenu li.toplevel_page_site-notices:focus { + background-color: inherit !important; + color: inherit !important; +} + +#adminmenu li.toplevel_page_site-notices .wp-menu-image { + display: none; +} + +#adminmenu .toplevel_page_site-notices .wp-menu-image { + border-radius: 2px; + background-color: #fff; +} + +#adminmenu .toplevel_page_site-notices .wp-menu-image:before { + content: '\f534'; + font-family: 'dashicons'; + font-size: 20px; + line-height: 20px; + background-color: #a7aaad; + color: white; + border-radius: 50%; + margin: 5px; + padding: 0; +} + +#adminmenu .toplevel_page_site-notices:hover .wp-menu-image:before { + color: #fff; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner { + display: flex; + flex-grow: 1; + flex-wrap: nowrap; + align-items: center; + justify-content: space-between; + position: relative; + width: 100%; + padding: 7px 12px; + right: -28px; + border-radius: 2px; + font-size: 12px; + line-height: 1.4; + -webkit-hyphens: none; + hyphens: none; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner .banner__info { + margin-left: 12px; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner .button { + font-size: 12px; + line-height: 12px; + padding: 0 7px; + border: 0; + min-height: 26px; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner svg.dismissible-card__close-icon { + height: 24px; + width: 24px; + margin-right: 10px; +} + +@media screen and (min-width: 782px) { + .folded #adminmenu .toplevel_page_site-notices .wp-menu-image { + display: block; + width: 30px; + } + + .folded #adminmenu .toplevel_page_site-notices { + height: 50px; + display: flex; + align-items: center; + justify-content: center; + } +} + +@media screen and (min-width: 782px) and (max-width: 960px){ + .auto-fold #adminmenu .toplevel_page_site-notices .wp-menu-image { + display: block; + width: 30px; + } + + .auto-fold #adminmenu .toplevel_page_site-notices { + height: 50px; + display: flex; + align-items: center; + justify-content: center; + } +} + +/* Prevent box-shadow at the top of the sidebar */ +#adminmenu .site-switcher:hover, +#adminmenu .toplevel_page_site-card:hover, +#adminmenu .toplevel_page_site-notices:hover { + box-shadow: none; +} + +/** + * Site icon inline-styles for height and width are defined in set_site_icon_inline_styles + */ +#adminmenu .toplevel_page_site-card .wp-menu-image { + background-image: none; + background-position: center; + background-repeat: no-repeat; + background-size: 18px 18px; + transform: translateZ(0); + transition-property: background-image,background-color; + transition-duration: .2s; +} + +#adminmenu a.toplevel_page_site-card:hover, +#adminmenu li.toplevel_page_site-card:hover { + background-color: inherit; +} + +#adminmenu .toplevel_page_site-card img { + opacity: initial; +} + +#adminmenu .toplevel_page_site-card.has-site-icon img { + padding: 0; +} + +#adminmenu .toplevel_page_site-card:hover div.wp-menu-image, +#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image { + background-image: url("data:image/svg+xml,%3Csvg class='gridicon gridicons-house' height='24' width='24' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg%3E%3Cpath fill='%23fff' d='M22 9L12 1 2 9v2h2v10h5v-4c0-1.657 1.343-3 3-3s3 1.343 3 3v4h5V11h2V9z'/%3E%3C/g%3E%3C/svg%3E%0A"); +} + +#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image { + background-color: #c3c4c7; +} + +#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image img[src^="data:image/svg"] { + height: auto; + padding-top: 7px; + width: 18px; +} + +#adminmenu .toplevel_page_site-card:hover div.wp-menu-image img, +#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image img { + display: none; +} + +.site__info .site__title { + display: block; + font-size: 14px; + font-weight: 400; + line-height: 1.3; +} + +.site__info .site__domain { + display: block; + max-width: 95%; + font-size: 12px; + line-height: 1.4; + margin-top: 2px; +} + +.site__info .site__title, +.site__info .site__domain { + overflow: hidden; + white-space: nowrap; +} +.site__info .site__title::after, +.site__info .site__domain::after { + content: ""; + display: block; + position: absolute; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; + pointer-events: none; + top: 0; + bottom: 0; + left: 0; + right: auto; + width: 20%; + height: auto; +} + +.site__info > .site__badge { + font-size: 12px; + border-radius: 12px; + clear: both; + display: inline-block; + margin-top: 6px; + margin-left: 3px; + padding: 1px 10px; +} + +/** + * Inline text in a menu title + */ +.inline-text { + display: block !important; + position: absolute; + left: 20px; + top: 50%; + transform: translateY( -50% ); + opacity: 0.8; +} + +/** + * Stats + */ +[class*="toplevel_page_https://wordpress.com/stats/day"] .sidebar-unified__sparkline { + float: left; + margin-left: 8px; +} + +/** + * Folded State + */ +.folded #adminmenu a.menu-top { + height: 31px; +} + +.folded #adminmenu li.toplevel_page_site-card a { + padding-right: 0; +} + +/* Auto-folding of the admin menu */ +@media only screen and (max-width: 960px) { + #adminmenu, + #adminmenuwrap, + #adminmenuback { + width: 272px; + } + + .auto-fold #adminmenu a[class*="toplevel_page_http"].wp-first-item { + height: auto; + } + + .wp-responsive-open #adminmenu a.menu-top { + height: auto; + } + + .auto-fold #adminmenu div.wp-menu-image { + width: 36px; + } +} + +@media screen and (min-width: 782px) and (max-width: 960px) { + .auto-fold #adminmenu a.menu-top { + height: 34px; + } + + .auto-fold #adminmenu li.toplevel_page_site-card a { + height: 36px; + padding-right: 1px; + } +} + +@media screen and (max-width: 782px) { + #adminmenu li.menu-top .wp-submenu>li>a, + .auto-fold #adminmenu li.menu-top .wp-submenu>li>a { + padding-right: 42px; + } + + .wp-responsive-open #wpbody { + left: inherit; + } + + .wp-responsive-open #wpcontent { + margin-right: 272px; + } + + .auto-fold #adminmenu, .auto-fold #adminmenuback, .auto-fold #adminmenuwrap { + width: 272px; + } + + .auto-fold #adminmenu a.site-switcher, + #adminmenu a.site-switcher { + font-size: 14px; + } +} + +@media only screen and (max-width: 660px) { + #adminmenuback, + #adminmenuwrap, + #adminmenu, + #adminmenu .wp-submenu, + .auto-fold #adminmenu, + .auto-fold #adminmenuback, + .auto-fold #adminmenuwrap { + width: 100%; + z-index: 171; + } + + .wp-responsive-open #wpcontent { + margin-right: 0; + } + + ul#adminmenu a.wp-has-current-submenu:after, + ul#adminmenu>li.current>a.current:after, + ul#adminmenu li:hover a.wp-has-current-submenu:after, + .auto-fold ul#adminmenu li:hover a.wp-has-current-submenu:after { + display: none; + } + + .auto-fold #adminmenu li.toplevel_page_site-card a { + padding: 18px 12px 18px 0; + } +} + +/* + * Styles for the nav-unification prototype (see pbAPfg-O2) + * TODO: depending on project outcome move or delete styles + */ +#wpadminbar #wp-admin-bar-notes #wpnt-notes-unread-count.wpn-unread { + top: 50%; + right: 50%; + transform: translate( 10px, -13px ); + +} + +#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar { + transform: translateX( -1px ); +} + +#wpadminbar #wp-admin-bar-notes.active .noticon-bell:before { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cmVjdCB4PSIwIiBmaWxsPSJub25lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiLz48Zz48cGF0aCBmaWxsPSIjZmZmZmZmIiBkPSJNNi4xNCAxNC45N2wyLjgyOCAyLjgyN2MtLjM2Mi4zNjItLjg2Mi41ODYtMS40MTQuNTg2LTEuMTA1IDAtMi0uODk1LTItMiAwLS41NTIuMjI0LTEuMDUyLjU4Ni0xLjQxNHptOC44NjcgNS4zMjRMMTQuMyAyMSAzIDkuN2wuNzA2LS43MDcgMS4xMDIuMTU3Yy43NTQuMTA4IDEuNjktLjEyMiAyLjA3Ny0uNTFsMy44ODUtMy44ODRjMi4zNC0yLjM0IDYuMTM1LTIuMzQgOC40NzUgMHMyLjM0IDYuMTM1IDAgOC40NzVsLTMuODg1IDMuODg2Yy0uMzg4LjM4OC0uNjE4IDEuMzIzLS41MSAyLjA3N2wuMTU3IDEuMXoiLz48L2c+PC9zdmc+") !important; +} + +#wpadminbar > #wp-toolbar .wpnt-show span.noticon, +#wpadminbar #wp-admin-bar-notes.wpnt-show .noticon { + color: #ffffff; +} + +#wpadminbar .quicklinks ul#wp-admin-bar-root-default { + padding-right: 0 !important; +} + +#wpadminbar #wp-admin-bar-menu-toggle { + display: none; +} + +@media screen and (max-width: 782px) { + #wpadminbar #wp-toolbar > ul > li { + display: block; + } + + #wpadminbar .ab-top-menu > li > .ab-item { + box-sizing: border-box; + line-height: 32px; + } + + #wpadminbar #wp-admin-bar-ab-new-post > .ab-item { + box-sizing: inherit !important; + } + + #wpadminbar #wp-admin-bar-my-account > .ab-item { + padding: 7px 15px; + width: auto; + } + + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + display: block; + left: auto; + right: auto; + position: static; + margin-top: 3px; + top: 13px; + } + + /* Hide debug bar. */ + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-debug-bar { + display: none; + } +} + +@media screen and (max-width: 480px) { + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar > a { + width: auto; + } + + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + margin-top: 12px; + } +} + +/* Fixes the order of screen options, + * showing the dashboard switcher button always first. +*/ +#adv-settings { + display: flex; + flex-direction: column; +} + +/* Switcher should always be in the top */ +#adv-settings #dashboard-switcher { + order: 0; +} + +#adv-settings .dashboard-switcher-text { + margin-top: 0; +} + +#adv-settings .dashboard-switcher-button { + padding: 3px 16px; +} + +/* Core registered options should be after the switcher */ +#adv-settings fieldset { + order: 1; +} + +/* Submit button should always be in the bottom */ +#adv-settings .submit { + order: 2; +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/admin-menu-rtl.min.css b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu-rtl.min.css new file mode 100644 index 00000000..63bf1689 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu-rtl.min.css @@ -0,0 +1 @@ +#adminmenu{margin:0}#wpcontent,#wpfooter{margin-right:272px}#adminmenu,#adminmenu .wp-submenu,#adminmenuback,#adminmenuwrap{width:272px}#adminmenu .wp-submenu{right:272px}#adminmenu .wp-not-current-submenu .wp-submenu,.folded #adminmenu .wp-has-current-submenu .wp-submenu{min-width:272px}@media (min-width:783px){.edit-post-layout .components-editor-notices__snackbar,.interface-interface-skeleton{right:272px}}@media (min-width:961px){.auto-fold .edit-post-layout .components-editor-notices__snackbar,.auto-fold .interface-interface-skeleton,.jp-dialogue-modern-full__container{right:272px}.global-notices{max-width:calc(100% - 48px - 272px)}}#adminmenu [class*=activity-log] .wp-menu-image img{padding-top:7px}#adminmenu .toplevel_page_site-card .wp-menu-name{margin-right:40px;padding:0}#adminmenu li.toplevel_page_site-card a{padding:10px 8px 10px 0}#adminmenu a.toplevel_page_site-notices:focus,#adminmenu a.toplevel_page_site-notices:hover,#adminmenu li.toplevel_page_site-notices:focus,#adminmenu li.toplevel_page_site-notices:hover{background-color:inherit!important;color:inherit!important}#adminmenu li.toplevel_page_site-notices .wp-menu-image{display:none}#adminmenu .toplevel_page_site-notices .wp-menu-image{border-radius:2px;background-color:#fff}#adminmenu .toplevel_page_site-notices .wp-menu-image:before{content:'\f534';font-family:dashicons;font-size:20px;line-height:20px;background-color:#a7aaad;color:#fff;border-radius:50%;margin:5px;padding:0}#adminmenu .toplevel_page_site-notices:hover .wp-menu-image:before{color:#fff}#adminmenu .toplevel_page_site-notices .upsell_banner{display:flex;flex-grow:1;flex-wrap:nowrap;align-items:center;justify-content:space-between;position:relative;width:100%;padding:7px 12px;right:-28px;border-radius:2px;font-size:12px;line-height:1.4;-webkit-hyphens:none;hyphens:none}#adminmenu .toplevel_page_site-notices .upsell_banner .banner__info{margin-left:12px}#adminmenu .toplevel_page_site-notices .upsell_banner .button{font-size:12px;line-height:12px;padding:0 7px;border:0;min-height:26px}#adminmenu .toplevel_page_site-notices .upsell_banner svg.dismissible-card__close-icon{height:24px;width:24px;margin-right:10px}@media screen and (min-width:782px){.folded #adminmenu .toplevel_page_site-notices .wp-menu-image{display:block;width:30px}.folded #adminmenu .toplevel_page_site-notices{height:50px;display:flex;align-items:center;justify-content:center}}@media screen and (min-width:782px) and (max-width:960px){.auto-fold #adminmenu .toplevel_page_site-notices .wp-menu-image{display:block;width:30px}.auto-fold #adminmenu .toplevel_page_site-notices{height:50px;display:flex;align-items:center;justify-content:center}}#adminmenu .site-switcher:hover,#adminmenu .toplevel_page_site-card:hover,#adminmenu .toplevel_page_site-notices:hover{box-shadow:none}#adminmenu .toplevel_page_site-card .wp-menu-image{background-image:none;background-position:center;background-repeat:no-repeat;background-size:18px 18px;transform:translateZ(0);transition-property:background-image,background-color;transition-duration:.2s}#adminmenu a.toplevel_page_site-card:hover,#adminmenu li.toplevel_page_site-card:hover{background-color:inherit}#adminmenu .toplevel_page_site-card img{opacity:initial}#adminmenu .toplevel_page_site-card.has-site-icon img{padding:0}#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image,#adminmenu .toplevel_page_site-card:hover div.wp-menu-image{background-image:url("data:image/svg+xml,%3Csvg class='gridicon gridicons-house' height='24' width='24' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg%3E%3Cpath fill='%23fff' d='M22 9L12 1 2 9v2h2v10h5v-4c0-1.657 1.343-3 3-3s3 1.343 3 3v4h5V11h2V9z'/%3E%3C/g%3E%3C/svg%3E%0A")}#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image{background-color:#c3c4c7}#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image img[src^="data:image/svg"]{height:auto;padding-top:7px;width:18px}#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image img,#adminmenu .toplevel_page_site-card:hover div.wp-menu-image img{display:none}.site__info .site__title{display:block;font-size:14px;font-weight:400;line-height:1.3}.site__info .site__domain{display:block;max-width:95%;font-size:12px;line-height:1.4;margin-top:2px}.site__info .site__domain,.site__info .site__title{overflow:hidden;white-space:nowrap}.site__info .site__domain::after,.site__info .site__title::after{content:"";display:block;position:absolute;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;pointer-events:none;top:0;bottom:0;left:0;right:auto;width:20%;height:auto}.site__info>.site__badge{font-size:12px;border-radius:12px;clear:both;display:inline-block;margin-top:6px;margin-left:3px;padding:1px 10px}.inline-text{display:block!important;position:absolute;left:20px;top:50%;transform:translateY(-50%);opacity:.8}[class*="toplevel_page_https://wordpress.com/stats/day"] .sidebar-unified__sparkline{float:left;margin-left:8px}.folded #adminmenu a.menu-top{height:31px}.folded #adminmenu li.toplevel_page_site-card a{padding-right:0}@media only screen and (max-width:960px){#adminmenu,#adminmenuback,#adminmenuwrap{width:272px}.auto-fold #adminmenu a[class*=toplevel_page_http].wp-first-item{height:auto}.wp-responsive-open #adminmenu a.menu-top{height:auto}.auto-fold #adminmenu div.wp-menu-image{width:36px}}@media screen and (min-width:782px) and (max-width:960px){.auto-fold #adminmenu a.menu-top{height:34px}.auto-fold #adminmenu li.toplevel_page_site-card a{height:36px;padding-right:1px}}@media screen and (max-width:782px){#adminmenu li.menu-top .wp-submenu>li>a,.auto-fold #adminmenu li.menu-top .wp-submenu>li>a{padding-right:42px}.wp-responsive-open #wpbody{left:inherit}.wp-responsive-open #wpcontent{margin-right:272px}.auto-fold #adminmenu,.auto-fold #adminmenuback,.auto-fold #adminmenuwrap{width:272px}#adminmenu a.site-switcher,.auto-fold #adminmenu a.site-switcher{font-size:14px}}@media only screen and (max-width:660px){#adminmenu,#adminmenu .wp-submenu,#adminmenuback,#adminmenuwrap,.auto-fold #adminmenu,.auto-fold #adminmenuback,.auto-fold #adminmenuwrap{width:100%;z-index:171}.wp-responsive-open #wpcontent{margin-right:0}.auto-fold ul#adminmenu li:hover a.wp-has-current-submenu:after,ul#adminmenu a.wp-has-current-submenu:after,ul#adminmenu li:hover a.wp-has-current-submenu:after,ul#adminmenu>li.current>a.current:after{display:none}.auto-fold #adminmenu li.toplevel_page_site-card a{padding:18px 12px 18px 0}}#wpadminbar #wp-admin-bar-notes #wpnt-notes-unread-count.wpn-unread{top:50%;right:50%;transform:translate(10px,-13px)}#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar{transform:translateX(-1px)}#wpadminbar #wp-admin-bar-notes.active .noticon-bell:before{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cmVjdCB4PSIwIiBmaWxsPSJub25lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiLz48Zz48cGF0aCBmaWxsPSIjZmZmZmZmIiBkPSJNNi4xNCAxNC45N2wyLjgyOCAyLjgyN2MtLjM2Mi4zNjItLjg2Mi41ODYtMS40MTQuNTg2LTEuMTA1IDAtMi0uODk1LTItMiAwLS41NTIuMjI0LTEuMDUyLjU4Ni0xLjQxNHptOC44NjcgNS4zMjRMMTQuMyAyMSAzIDkuN2wuNzA2LS43MDcgMS4xMDIuMTU3Yy43NTQuMTA4IDEuNjktLjEyMiAyLjA3Ny0uNTFsMy44ODUtMy44ODRjMi4zNC0yLjM0IDYuMTM1LTIuMzQgOC40NzUgMHMyLjM0IDYuMTM1IDAgOC40NzVsLTMuODg1IDMuODg2Yy0uMzg4LjM4OC0uNjE4IDEuMzIzLS41MSAyLjA3N2wuMTU3IDEuMXoiLz48L2c+PC9zdmc+)!important}#wpadminbar #wp-admin-bar-notes.wpnt-show .noticon,#wpadminbar>#wp-toolbar .wpnt-show span.noticon{color:#fff}#wpadminbar .quicklinks ul#wp-admin-bar-root-default{padding-right:0!important}#wpadminbar #wp-admin-bar-menu-toggle{display:none}@media screen and (max-width:782px){#wpadminbar #wp-toolbar>ul>li{display:block}#wpadminbar .ab-top-menu>li>.ab-item{box-sizing:border-box;line-height:32px}#wpadminbar #wp-admin-bar-ab-new-post>.ab-item{box-sizing:inherit!important}#wpadminbar #wp-admin-bar-my-account>.ab-item{padding:7px 15px;width:auto}#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar>a img{display:block;left:auto;right:auto;position:static;margin-top:3px;top:13px}#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-debug-bar{display:none}}@media screen and (max-width:480px){#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar>a{width:auto}#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar>a img{margin-top:12px}}#adv-settings{display:flex;flex-direction:column}#adv-settings #dashboard-switcher{order:0}#adv-settings .dashboard-switcher-text{margin-top:0}#adv-settings .dashboard-switcher-button{padding:3px 16px}#adv-settings fieldset{order:1}#adv-settings .submit{order:2}
\ No newline at end of file diff --git a/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.css b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.css new file mode 100644 index 00000000..92b762cb --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.css @@ -0,0 +1,488 @@ +#adminmenu { + margin: 0; +} + +/** + * Menu width + */ +#wpcontent, +#wpfooter { + margin-left: 272px; +} + +#adminmenuback, +#adminmenuwrap, +#adminmenu, +#adminmenu .wp-submenu { + width: 272px; +} + +#adminmenu .wp-submenu { + left: 272px; +} + +#adminmenu .wp-not-current-submenu .wp-submenu, +.folded #adminmenu .wp-has-current-submenu .wp-submenu { + min-width: 272px; +} + +/** + * Fixes Gutenberg in not fullscreen mode. + */ + @media (min-width: 783px) { + .interface-interface-skeleton, + .edit-post-layout .components-editor-notices__snackbar { + left: 272px; + } +} + +@media (min-width: 961px) { + .auto-fold .interface-interface-skeleton, + .auto-fold .edit-post-layout .components-editor-notices__snackbar, + .jp-dialogue-modern-full__container { + left: 272px; + } + + .global-notices { + max-width: calc( 100% - 48px - 272px ); + } +} + +/** + * Jetpack logo + */ +#adminmenu [class*="activity-log"] .wp-menu-image img { + padding-top: 7px; +} + +/** + * Site Card + */ +#adminmenu .toplevel_page_site-card .wp-menu-name { + margin-left: 40px; /* icon width (32) + padding (8). */ + padding: 0; +} + +#adminmenu li.toplevel_page_site-card a { + padding: 10px 0 10px 8px; +} + +/** + * Site Notices + */ +#adminmenu a.toplevel_page_site-notices:hover, +#adminmenu a.toplevel_page_site-notices:focus, +#adminmenu li.toplevel_page_site-notices:hover, +#adminmenu li.toplevel_page_site-notices:focus { + background-color: inherit !important; + color: inherit !important; +} + +#adminmenu li.toplevel_page_site-notices .wp-menu-image { + display: none; +} + +#adminmenu .toplevel_page_site-notices .wp-menu-image { + border-radius: 2px; + background-color: #fff; +} + +#adminmenu .toplevel_page_site-notices .wp-menu-image:before { + content: '\f534'; + font-family: 'dashicons'; + font-size: 20px; + line-height: 20px; + background-color: #a7aaad; + color: white; + border-radius: 50%; + margin: 5px; + padding: 0; +} + +#adminmenu .toplevel_page_site-notices:hover .wp-menu-image:before { + color: #fff; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner { + display: flex; + flex-grow: 1; + flex-wrap: nowrap; + align-items: center; + justify-content: space-between; + position: relative; + width: 100%; + padding: 7px 12px; + left: -28px; + border-radius: 2px; + font-size: 12px; + line-height: 1.4; + hyphens: none; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner .banner__info { + margin-right: 12px; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner .button { + font-size: 12px; + line-height: 12px; + padding: 0 7px; + border: 0; + min-height: 26px; +} + +#adminmenu .toplevel_page_site-notices .upsell_banner svg.dismissible-card__close-icon { + height: 24px; + width: 24px; + margin-left: 10px; +} + +@media screen and (min-width: 782px) { + .folded #adminmenu .toplevel_page_site-notices .wp-menu-image { + display: block; + width: 30px; + } + + .folded #adminmenu .toplevel_page_site-notices { + height: 50px; + display: flex; + align-items: center; + justify-content: center; + } +} + +@media screen and (min-width: 782px) and (max-width: 960px){ + .auto-fold #adminmenu .toplevel_page_site-notices .wp-menu-image { + display: block; + width: 30px; + } + + .auto-fold #adminmenu .toplevel_page_site-notices { + height: 50px; + display: flex; + align-items: center; + justify-content: center; + } +} + +/* Prevent box-shadow at the top of the sidebar */ +#adminmenu .site-switcher:hover, +#adminmenu .toplevel_page_site-card:hover, +#adminmenu .toplevel_page_site-notices:hover { + box-shadow: none; +} + +/** + * Site icon inline-styles for height and width are defined in set_site_icon_inline_styles + */ +#adminmenu .toplevel_page_site-card .wp-menu-image { + background-image: none; + background-position: center; + background-repeat: no-repeat; + background-size: 18px 18px; + transform: translateZ(0); + transition-property: background-image,background-color; + transition-duration: .2s; +} + +#adminmenu a.toplevel_page_site-card:hover, +#adminmenu li.toplevel_page_site-card:hover { + background-color: inherit; +} + +#adminmenu .toplevel_page_site-card img { + opacity: initial; +} + +#adminmenu .toplevel_page_site-card.has-site-icon img { + padding: 0; +} + +#adminmenu .toplevel_page_site-card:hover div.wp-menu-image, +#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image { + background-image: url("data:image/svg+xml,%3Csvg class='gridicon gridicons-house' height='24' width='24' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg%3E%3Cpath fill='%23fff' d='M22 9L12 1 2 9v2h2v10h5v-4c0-1.657 1.343-3 3-3s3 1.343 3 3v4h5V11h2V9z'/%3E%3C/g%3E%3C/svg%3E%0A"); +} + +#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image { + background-color: #c3c4c7; +} + +#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image img[src^="data:image/svg"] { + height: auto; + padding-top: 7px; + width: 18px; +} + +#adminmenu .toplevel_page_site-card:hover div.wp-menu-image img, +#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image img { + display: none; +} + +.site__info .site__title { + display: block; + font-size: 14px; + font-weight: 400; + line-height: 1.3; +} + +.site__info .site__domain { + display: block; + max-width: 95%; + font-size: 12px; + line-height: 1.4; + margin-top: 2px; +} + +.site__info .site__title, +.site__info .site__domain { + overflow: hidden; + white-space: nowrap; +} +.site__info .site__title::after, +.site__info .site__domain::after { + content: ""; + display: block; + position: absolute; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; + pointer-events: none; + top: 0; + bottom: 0; + right: 0; + left: auto; + width: 20%; + height: auto; +} + +.site__info > .site__badge { + font-size: 12px; + border-radius: 12px; + clear: both; + display: inline-block; + margin-top: 6px; + margin-right: 3px; + padding: 1px 10px; +} + +/** + * Inline text in a menu title + */ +.inline-text { + display: block !important; + position: absolute; + right: 20px; + top: 50%; + transform: translateY( -50% ); + opacity: 0.8; +} + +/** + * Stats + */ +[class*="toplevel_page_https://wordpress.com/stats/day"] .sidebar-unified__sparkline { + float: right; + margin-right: 8px; +} + +/** + * Folded State + */ +.folded #adminmenu a.menu-top { + height: 31px; +} + +.folded #adminmenu li.toplevel_page_site-card a { + padding-left: 0; +} + +/* Auto-folding of the admin menu */ +@media only screen and (max-width: 960px) { + #adminmenu, + #adminmenuwrap, + #adminmenuback { + width: 272px; + } + + .auto-fold #adminmenu a[class*="toplevel_page_http"].wp-first-item { + height: auto; + } + + .wp-responsive-open #adminmenu a.menu-top { + height: auto; + } + + .auto-fold #adminmenu div.wp-menu-image { + width: 36px; + } +} + +@media screen and (min-width: 782px) and (max-width: 960px) { + .auto-fold #adminmenu a.menu-top { + height: 34px; + } + + .auto-fold #adminmenu li.toplevel_page_site-card a { + height: 36px; + padding-left: 1px; + } +} + +@media screen and (max-width: 782px) { + #adminmenu li.menu-top .wp-submenu>li>a, + .auto-fold #adminmenu li.menu-top .wp-submenu>li>a { + padding-left: 42px; + } + + .wp-responsive-open #wpbody { + right: inherit; + } + + .wp-responsive-open #wpcontent { + margin-left: 272px; + } + + .auto-fold #adminmenu, .auto-fold #adminmenuback, .auto-fold #adminmenuwrap { + width: 272px; + } + + .auto-fold #adminmenu a.site-switcher, + #adminmenu a.site-switcher { + font-size: 14px; + } +} + +@media only screen and (max-width: 660px) { + #adminmenuback, + #adminmenuwrap, + #adminmenu, + #adminmenu .wp-submenu, + .auto-fold #adminmenu, + .auto-fold #adminmenuback, + .auto-fold #adminmenuwrap { + width: 100%; + z-index: 171; + } + + .wp-responsive-open #wpcontent { + margin-left: 0; + } + + ul#adminmenu a.wp-has-current-submenu:after, + ul#adminmenu>li.current>a.current:after, + ul#adminmenu li:hover a.wp-has-current-submenu:after, + .auto-fold ul#adminmenu li:hover a.wp-has-current-submenu:after { + display: none; + } + + .auto-fold #adminmenu li.toplevel_page_site-card a { + padding: 18px 0 18px 12px; + } +} + +/* + * Styles for the nav-unification prototype (see pbAPfg-O2) + * TODO: depending on project outcome move or delete styles + */ +#wpadminbar #wp-admin-bar-notes #wpnt-notes-unread-count.wpn-unread { + top: 50%; + left: 50%; + transform: translate( -10px, -13px ); + +} + +#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar { + transform: translateX( 1px ); +} + +#wpadminbar #wp-admin-bar-notes.active .noticon-bell:before { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cmVjdCB4PSIwIiBmaWxsPSJub25lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiLz48Zz48cGF0aCBmaWxsPSIjZmZmZmZmIiBkPSJNNi4xNCAxNC45N2wyLjgyOCAyLjgyN2MtLjM2Mi4zNjItLjg2Mi41ODYtMS40MTQuNTg2LTEuMTA1IDAtMi0uODk1LTItMiAwLS41NTIuMjI0LTEuMDUyLjU4Ni0xLjQxNHptOC44NjcgNS4zMjRMMTQuMyAyMSAzIDkuN2wuNzA2LS43MDcgMS4xMDIuMTU3Yy43NTQuMTA4IDEuNjktLjEyMiAyLjA3Ny0uNTFsMy44ODUtMy44ODRjMi4zNC0yLjM0IDYuMTM1LTIuMzQgOC40NzUgMHMyLjM0IDYuMTM1IDAgOC40NzVsLTMuODg1IDMuODg2Yy0uMzg4LjM4OC0uNjE4IDEuMzIzLS41MSAyLjA3N2wuMTU3IDEuMXoiLz48L2c+PC9zdmc+") !important; +} + +#wpadminbar > #wp-toolbar .wpnt-show span.noticon, +#wpadminbar #wp-admin-bar-notes.wpnt-show .noticon { + color: #ffffff; +} + +#wpadminbar .quicklinks ul#wp-admin-bar-root-default { + padding-left: 0 !important; +} + +#wpadminbar #wp-admin-bar-menu-toggle { + display: none; +} + +@media screen and (max-width: 782px) { + #wpadminbar #wp-toolbar > ul > li { + display: block; + } + + #wpadminbar .ab-top-menu > li > .ab-item { + box-sizing: border-box; + line-height: 32px; + } + + #wpadminbar #wp-admin-bar-ab-new-post > .ab-item { + box-sizing: inherit !important; + } + + #wpadminbar #wp-admin-bar-my-account > .ab-item { + padding: 7px 15px; + width: auto; + } + + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + display: block; + right: auto; + left: auto; + position: static; + margin-top: 3px; + top: 13px; + } + + /* Hide debug bar. */ + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-debug-bar { + display: none; + } +} + +@media screen and (max-width: 480px) { + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar > a { + width: auto; + } + + #wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + margin-top: 12px; + } +} + +/* Fixes the order of screen options, + * showing the dashboard switcher button always first. +*/ +#adv-settings { + display: flex; + flex-direction: column; +} + +/* Switcher should always be in the top */ +#adv-settings #dashboard-switcher { + order: 0; +} + +#adv-settings .dashboard-switcher-text { + margin-top: 0; +} + +#adv-settings .dashboard-switcher-button { + padding: 3px 16px; +} + +/* Core registered options should be after the switcher */ +#adv-settings fieldset { + order: 1; +} + +/* Submit button should always be in the bottom */ +#adv-settings .submit { + order: 2; +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.js b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.js new file mode 100644 index 00000000..ce385913 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.js @@ -0,0 +1,124 @@ +/* global ajaxurl, jetpackAdminMenu */ + +( function () { + function init() { + var adminbar = document.querySelector( '#wpadminbar' ); + var wpwrap = document.querySelector( '#wpwrap' ); + var adminMenu = document.querySelector( '#adminmenu' ); + + if ( ! adminbar ) { + return; + } + + function setAriaExpanded( value ) { + var anchors = adminbar.querySelectorAll( '#wp-admin-bar-blog a' ); + for ( var i = 0; i < anchors.length; i++ ) { + anchors[ i ].setAttribute( 'aria-expanded', value ); + } + } + + setFocusOnActiveMenuItem(); + setAriaExpanded( 'false' ); + + var adminbarBlog = adminbar.querySelector( '#wp-admin-bar-blog' ); + // Toggle sidebar when toggle is clicked. + if ( adminbarBlog ) { + adminbarBlog.addEventListener( 'click', function ( event ) { + event.preventDefault(); + + // Close any open toolbar submenus. + var hovers = adminbar.querySelectorAll( '.hover' ); + for ( var i = 0; i < hovers.length; i++ ) { + hovers[ i ].classList.remove( 'hover' ); + } + + wpwrap.classList.toggle( 'wp-responsive-open' ); + if ( wpwrap.classList.contains( 'wp-responsive-open' ) ) { + setAriaExpanded( 'true' ); + var first = document.querySelector( '#adminmenu a' ); + if ( first ) { + first.focus(); + } + } else { + setAriaExpanded( 'false' ); + } + } ); + } + + if ( adminMenu ) { + var collapseButton = adminMenu.querySelector( '#collapse-button' ); + // Nav-Unification feature: + // Saves the sidebar state in server when "Collapse menu" is clicked. + // This is needed so that we update WPCOM for this preference in real-time. + if ( collapseButton ) { + collapseButton.addEventListener( 'click', function ( event ) { + // Let's the core event listener be triggered first. + setTimeout( function () { + saveSidebarIsExpanded( event.target.parentNode.ariaExpanded ); + }, 50 ); + } ); + } + + const jitmDismissButton = adminMenu.querySelector( '.dismissible-card__close-icon' ); + if ( jitmDismissButton ) { + jitmDismissButton.addEventListener( 'click', function ( event ) { + event.preventDefault(); + + const siteNotice = document.getElementById( 'toplevel_page_site-notices' ); + if ( siteNotice ) { + siteNotice.style.display = 'none'; + } + + makeAjaxRequest( + 'POST', + ajaxurl, + 'application/x-www-form-urlencoded; charset=UTF-8', + 'id=' + + encodeURIComponent( jitmDismissButton.dataset.feature_id ) + + '&feature_class=' + + encodeURIComponent( jitmDismissButton.dataset.feature_class ) + + '&action=jitm_dismiss' + + '&_ajax_nonce=' + + jetpackAdminMenu.jitmDismissNonce + ); + } ); + } + } + } + + function makeAjaxRequest( method, url, contentType, body ) { + var xhr = new XMLHttpRequest(); + xhr.open( method, url, true ); + xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' ); + if ( contentType ) { + xhr.setRequestHeader( 'Content-Type', contentType ); + } + xhr.withCredentials = true; + xhr.send( body ); + } + + function saveSidebarIsExpanded( expanded ) { + makeAjaxRequest( + 'POST', + ajaxurl, + 'application/x-www-form-urlencoded; charset=UTF-8', + 'action=sidebar_state&expanded=' + expanded + ); + } + + function setFocusOnActiveMenuItem() { + var currentMenuItem = document.querySelector( '.wp-submenu .current > a' ); + + if ( ! currentMenuItem ) { + return; + } + + currentMenuItem.focus(); + } + + if ( document.readyState === 'loading' ) { + document.addEventListener( 'DOMContentLoaded', init ); + } else { + init(); + } +} )(); diff --git a/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.min.css b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.min.css new file mode 100644 index 00000000..1b50ea73 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/admin-menu.min.css @@ -0,0 +1,2 @@ +/* Do not modify this file directly. It is concatenated from individual module CSS files. */ +#adminmenu{margin:0}#wpcontent,#wpfooter{margin-left:272px}#adminmenu,#adminmenu .wp-submenu,#adminmenuback,#adminmenuwrap{width:272px}#adminmenu .wp-submenu{left:272px}#adminmenu .wp-not-current-submenu .wp-submenu,.folded #adminmenu .wp-has-current-submenu .wp-submenu{min-width:272px}@media (min-width:783px){.edit-post-layout .components-editor-notices__snackbar,.interface-interface-skeleton{left:272px}}@media (min-width:961px){.auto-fold .edit-post-layout .components-editor-notices__snackbar,.auto-fold .interface-interface-skeleton,.jp-dialogue-modern-full__container{left:272px}.global-notices{max-width:calc(100% - 48px - 272px)}}#adminmenu [class*=activity-log] .wp-menu-image img{padding-top:7px}#adminmenu .toplevel_page_site-card .wp-menu-name{margin-left:40px;padding:0}#adminmenu li.toplevel_page_site-card a{padding:10px 0 10px 8px}#adminmenu a.toplevel_page_site-notices:focus,#adminmenu a.toplevel_page_site-notices:hover,#adminmenu li.toplevel_page_site-notices:focus,#adminmenu li.toplevel_page_site-notices:hover{background-color:inherit!important;color:inherit!important}#adminmenu li.toplevel_page_site-notices .wp-menu-image{display:none}#adminmenu .toplevel_page_site-notices .wp-menu-image{border-radius:2px;background-color:#fff}#adminmenu .toplevel_page_site-notices .wp-menu-image:before{content:'\f534';font-family:dashicons;font-size:20px;line-height:20px;background-color:#a7aaad;color:#fff;border-radius:50%;margin:5px;padding:0}#adminmenu .toplevel_page_site-notices:hover .wp-menu-image:before{color:#fff}#adminmenu .toplevel_page_site-notices .upsell_banner{display:flex;flex-grow:1;flex-wrap:nowrap;align-items:center;justify-content:space-between;position:relative;width:100%;padding:7px 12px;left:-28px;border-radius:2px;font-size:12px;line-height:1.4;-webkit-hyphens:none;hyphens:none}#adminmenu .toplevel_page_site-notices .upsell_banner .banner__info{margin-right:12px}#adminmenu .toplevel_page_site-notices .upsell_banner .button{font-size:12px;line-height:12px;padding:0 7px;border:0;min-height:26px}#adminmenu .toplevel_page_site-notices .upsell_banner svg.dismissible-card__close-icon{height:24px;width:24px;margin-left:10px}@media screen and (min-width:782px){.folded #adminmenu .toplevel_page_site-notices .wp-menu-image{display:block;width:30px}.folded #adminmenu .toplevel_page_site-notices{height:50px;display:flex;align-items:center;justify-content:center}}@media screen and (min-width:782px) and (max-width:960px){.auto-fold #adminmenu .toplevel_page_site-notices .wp-menu-image{display:block;width:30px}.auto-fold #adminmenu .toplevel_page_site-notices{height:50px;display:flex;align-items:center;justify-content:center}}#adminmenu .site-switcher:hover,#adminmenu .toplevel_page_site-card:hover,#adminmenu .toplevel_page_site-notices:hover{box-shadow:none}#adminmenu .toplevel_page_site-card .wp-menu-image{background-image:none;background-position:center;background-repeat:no-repeat;background-size:18px 18px;transform:translateZ(0);transition-property:background-image,background-color;transition-duration:.2s}#adminmenu a.toplevel_page_site-card:hover,#adminmenu li.toplevel_page_site-card:hover{background-color:inherit}#adminmenu .toplevel_page_site-card img{opacity:initial}#adminmenu .toplevel_page_site-card.has-site-icon img{padding:0}#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image,#adminmenu .toplevel_page_site-card:hover div.wp-menu-image{background-image:url("data:image/svg+xml,%3Csvg class='gridicon gridicons-house' height='24' width='24' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg%3E%3Cpath fill='%23fff' d='M22 9L12 1 2 9v2h2v10h5v-4c0-1.657 1.343-3 3-3s3 1.343 3 3v4h5V11h2V9z'/%3E%3C/g%3E%3C/svg%3E%0A")}#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image{background-color:#c3c4c7}#adminmenu .toplevel_page_site-card:not(.has-site-icon) .wp-menu-image img[src^="data:image/svg"]{height:auto;padding-top:7px;width:18px}#adminmenu .toplevel_page_site-card a:focus div.wp-menu-image img,#adminmenu .toplevel_page_site-card:hover div.wp-menu-image img{display:none}.site__info .site__title{display:block;font-size:14px;font-weight:400;line-height:1.3}.site__info .site__domain{display:block;max-width:95%;font-size:12px;line-height:1.4;margin-top:2px}.site__info .site__domain,.site__info .site__title{overflow:hidden;white-space:nowrap}.site__info .site__domain::after,.site__info .site__title::after{content:"";display:block;position:absolute;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;pointer-events:none;top:0;bottom:0;right:0;left:auto;width:20%;height:auto}.site__info>.site__badge{font-size:12px;border-radius:12px;clear:both;display:inline-block;margin-top:6px;margin-right:3px;padding:1px 10px}.inline-text{display:block!important;position:absolute;right:20px;top:50%;transform:translateY(-50%);opacity:.8}[class*="toplevel_page_https://wordpress.com/stats/day"] .sidebar-unified__sparkline{float:right;margin-right:8px}.folded #adminmenu a.menu-top{height:31px}.folded #adminmenu li.toplevel_page_site-card a{padding-left:0}@media only screen and (max-width:960px){#adminmenu,#adminmenuback,#adminmenuwrap{width:272px}.auto-fold #adminmenu a[class*=toplevel_page_http].wp-first-item{height:auto}.wp-responsive-open #adminmenu a.menu-top{height:auto}.auto-fold #adminmenu div.wp-menu-image{width:36px}}@media screen and (min-width:782px) and (max-width:960px){.auto-fold #adminmenu a.menu-top{height:34px}.auto-fold #adminmenu li.toplevel_page_site-card a{height:36px;padding-left:1px}}@media screen and (max-width:782px){#adminmenu li.menu-top .wp-submenu>li>a,.auto-fold #adminmenu li.menu-top .wp-submenu>li>a{padding-left:42px}.wp-responsive-open #wpbody{right:inherit}.wp-responsive-open #wpcontent{margin-left:272px}.auto-fold #adminmenu,.auto-fold #adminmenuback,.auto-fold #adminmenuwrap{width:272px}#adminmenu a.site-switcher,.auto-fold #adminmenu a.site-switcher{font-size:14px}}@media only screen and (max-width:660px){#adminmenu,#adminmenu .wp-submenu,#adminmenuback,#adminmenuwrap,.auto-fold #adminmenu,.auto-fold #adminmenuback,.auto-fold #adminmenuwrap{width:100%;z-index:171}.wp-responsive-open #wpcontent{margin-left:0}.auto-fold ul#adminmenu li:hover a.wp-has-current-submenu:after,ul#adminmenu a.wp-has-current-submenu:after,ul#adminmenu li:hover a.wp-has-current-submenu:after,ul#adminmenu>li.current>a.current:after{display:none}.auto-fold #adminmenu li.toplevel_page_site-card a{padding:18px 0 18px 12px}}#wpadminbar #wp-admin-bar-notes #wpnt-notes-unread-count.wpn-unread{top:50%;left:50%;transform:translate(-10px,-13px)}#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar{transform:translateX(1px)}#wpadminbar #wp-admin-bar-notes.active .noticon-bell:before{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cmVjdCB4PSIwIiBmaWxsPSJub25lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiLz48Zz48cGF0aCBmaWxsPSIjZmZmZmZmIiBkPSJNNi4xNCAxNC45N2wyLjgyOCAyLjgyN2MtLjM2Mi4zNjItLjg2Mi41ODYtMS40MTQuNTg2LTEuMTA1IDAtMi0uODk1LTItMiAwLS41NTIuMjI0LTEuMDUyLjU4Ni0xLjQxNHptOC44NjcgNS4zMjRMMTQuMyAyMSAzIDkuN2wuNzA2LS43MDcgMS4xMDIuMTU3Yy43NTQuMTA4IDEuNjktLjEyMiAyLjA3Ny0uNTFsMy44ODUtMy44ODRjMi4zNC0yLjM0IDYuMTM1LTIuMzQgOC40NzUgMHMyLjM0IDYuMTM1IDAgOC40NzVsLTMuODg1IDMuODg2Yy0uMzg4LjM4OC0uNjE4IDEuMzIzLS41MSAyLjA3N2wuMTU3IDEuMXoiLz48L2c+PC9zdmc+)!important}#wpadminbar #wp-admin-bar-notes.wpnt-show .noticon,#wpadminbar>#wp-toolbar .wpnt-show span.noticon{color:#fff}#wpadminbar .quicklinks ul#wp-admin-bar-root-default{padding-left:0!important}#wpadminbar #wp-admin-bar-menu-toggle{display:none}@media screen and (max-width:782px){#wpadminbar #wp-toolbar>ul>li{display:block}#wpadminbar .ab-top-menu>li>.ab-item{box-sizing:border-box;line-height:32px}#wpadminbar #wp-admin-bar-ab-new-post>.ab-item{box-sizing:inherit!important}#wpadminbar #wp-admin-bar-my-account>.ab-item{padding:7px 15px;width:auto}#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar>a img{display:block;right:auto;left:auto;position:static;margin-top:3px;top:13px}#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-debug-bar{display:none}}@media screen and (max-width:480px){#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar>a{width:auto}#wpadminbar #wp-toolbar.quicklinks li#wp-admin-bar-my-account.with-avatar>a img{margin-top:12px}}#adv-settings{display:flex;flex-direction:column}#adv-settings #dashboard-switcher{order:0}#adv-settings .dashboard-switcher-text{margin-top:0}#adv-settings .dashboard-switcher-button{padding:3px 16px}#adv-settings fieldset{order:1}#adv-settings .submit{order:2}
\ No newline at end of file diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-admin-menu.php b/plugins/jetpack/modules/masterbar/admin-menu/class-admin-menu.php new file mode 100644 index 00000000..46b7f8bf --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-admin-menu.php @@ -0,0 +1,542 @@ +<?php +/** + * Admin Menu file. + * + * @package automattic/jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +use Automattic\Jetpack\Redirect; + +require_once __DIR__ . '/class-base-admin-menu.php'; + +/** + * Class Admin_Menu. + */ +class Admin_Menu extends Base_Admin_Menu { + + /** + * Create the desired menu output. + */ + public function reregister_menu_items() { + // Remove separators. + remove_menu_page( 'separator1' ); + + $this->add_stats_menu(); + $this->add_upgrades_menu(); + $this->add_posts_menu(); + $this->add_media_menu(); + $this->add_page_menu(); + $this->add_testimonials_menu(); + $this->add_portfolio_menu(); + $this->add_comments_menu(); + $this->add_appearance_menu(); + $this->add_plugins_menu(); + $this->add_users_menu(); + $this->add_tools_menu(); + $this->add_options_menu(); + $this->add_jetpack_menu(); + $this->add_gutenberg_menus(); + + // Remove Links Manager menu since its usage is discouraged. https://github.com/Automattic/wp-calypso/issues/51188. + // @see https://core.trac.wordpress.org/ticket/21307#comment:73. + if ( $this->should_disable_links_manager() ) { + remove_menu_page( 'link-manager.php' ); + } + + ksort( $GLOBALS['menu'] ); + } + + /** + * Get the preferred view for the given screen. + * + * @param string $screen Screen identifier. + * @param bool $fallback_global_preference (Optional) Whether the global preference for all screens should be used + * as fallback if there is no specific preference for the given screen. + * Default: true. + * @return string + */ + public function get_preferred_view( $screen, $fallback_global_preference = true ) { + // When no preferred view has been set for "Users > All Users" or "Settings > General", keep the previous + // behavior that forced the default view regardless of the global preference. + if ( + $fallback_global_preference && + in_array( $screen, array( 'users.php', 'options-general.php' ), true ) + ) { + $preferred_view = parent::get_preferred_view( $screen, false ); + if ( self::UNKNOWN_VIEW === $preferred_view ) { + return self::DEFAULT_VIEW; + } + return $preferred_view; + } + + return parent::get_preferred_view( $screen, $fallback_global_preference ); + } + + /** + * Check if Links Manager is being used. + */ + public function should_disable_links_manager() { + // The max ID number of the auto-generated links. + // See /wp-content/mu-plugins/wpcom-wp-install-defaults.php in WP.com. + $max_default_id = 10; + + // We are only checking the latest entry link_id so are limiting the query to 1. + $link_manager_links = get_bookmarks( + array( + 'orderby' => 'link_id', + 'order' => 'DESC', + 'limit' => 1, + 'hide_invisible' => 0, + ) + ); + + // Ordered links by ID descending, check if the first ID is more than $max_default_id. + if ( count( $link_manager_links ) > 0 && $link_manager_links[0]->link_id > $max_default_id ) { + return false; + } + + return true; + } + + /** + * Adds My Home menu. + */ + public function add_my_home_menu() { + $this->update_menu( 'index.php', 'https://wordpress.com/home/' . $this->domain, __( 'My Home', 'jetpack' ), 'edit_posts', 'dashicons-admin-home' ); + } + + /** + * Adds upsell nudge as a menu. + * + * @param object $nudge The $nudge object containing the content, CTA, link and tracks. + */ + public function add_upsell_nudge( $nudge ) { + $dismiss_button = ''; + if ( $nudge['dismissible'] ) { + $dismiss_button = '<svg xmlns="http://www.w3.org/2000/svg" data-feature_class="%1$s" data-feature_id="%2$s" viewBox="0 0 24 24" class="gridicon gridicons-cross dismissible-card__close-icon" height="24" width="24"><g><path d="M18.36 19.78L12 13.41l-6.36 6.37-1.42-1.42L10.59 12 4.22 5.64l1.42-1.42L12 10.59l6.36-6.36 1.41 1.41L13.41 12l6.36 6.36z"></path></g></svg>'; + $dismiss_button = sprintf( $dismiss_button, esc_attr( $nudge['feature_class'] ), esc_attr( $nudge['id'] ) ); + } + + $message = ' +<div class="upsell_banner"> + <div class="banner__info"> + <div class="banner__title">%1$s</div> + </div> + <div class="banner__action"> + <button type="button" class="button">%2$s</button> + </div>%3$s +</div>'; + + $message = sprintf( + $message, + wp_kses( $nudge['content'], array() ), + wp_kses( $nudge['cta'], array() ), + $dismiss_button + ); + + $menu_slug = $nudge['link']; + if ( wp_startswith( $menu_slug, '/' ) ) { + $menu_slug = 'https://wordpress.com' . $menu_slug; + } + + add_menu_page( 'site-notices', $message, 'read', $menu_slug, null, null, 1 ); + add_filter( 'add_menu_classes', array( $this, 'set_site_notices_menu_class' ) ); + } + + /** + * Adds a custom element class and id for Site Notices's menu item. + * + * @param array $menu Associative array of administration menu items. + * @return array + */ + public function set_site_notices_menu_class( array $menu ) { + foreach ( $menu as $key => $menu_item ) { + if ( 'site-notices' !== $menu_item[3] ) { + continue; + } + + $classes = ' toplevel_page_site-notices'; + + if ( isset( $menu_item[4] ) ) { + $menu[ $key ][4] = $menu_item[4] . $classes; + $menu[ $key ][5] = 'toplevel_page_site-notices'; + break; + } + } + + return $menu; + } + + /** + * Adds Inbox menu. + */ + public function add_inbox_menu() { + add_menu_page( __( 'Inbox', 'jetpack' ), __( 'Inbox', 'jetpack' ), 'manage_options', 'https://wordpress.com/inbox/' . $this->domain, null, 'dashicons-email', '4.64424' ); + } + + /** + * Adds Stats menu. + */ + public function add_stats_menu() { + add_menu_page( __( 'Stats', 'jetpack' ), __( 'Stats', 'jetpack' ), 'view_stats', 'https://wordpress.com/stats/day/' . $this->domain, null, 'dashicons-chart-bar', 3 ); + } + + /** + * Adds Upgrades menu. + * + * @param string $plan The current WPCOM plan of the blog. + */ + public function add_upgrades_menu( $plan = null ) { + global $menu; + + $menu_exists = false; + foreach ( $menu as $item ) { + if ( 'paid-upgrades.php' === $item[2] ) { + $menu_exists = true; + break; + } + } + + if ( ! $menu_exists ) { + if ( $plan ) { + // Add display:none as a default for cases when CSS is not loaded. + $site_upgrades = '%1$s<span class="inline-text" style="display:none">%2$s</span>'; + $site_upgrades = sprintf( + $site_upgrades, + __( 'Upgrades', 'jetpack' ), + $plan + ); + } else { + $site_upgrades = __( 'Upgrades', 'jetpack' ); + } + + add_menu_page( __( 'Upgrades', 'jetpack' ), $site_upgrades, 'manage_options', 'paid-upgrades.php', null, 'dashicons-cart', 4 ); + } + + add_submenu_page( 'paid-upgrades.php', __( 'Plans', 'jetpack' ), __( 'Plans', 'jetpack' ), 'manage_options', 'https://wordpress.com/plans/' . $this->domain, null, 1 ); + add_submenu_page( 'paid-upgrades.php', __( 'Purchases', 'jetpack' ), __( 'Purchases', 'jetpack' ), 'manage_options', 'https://wordpress.com/purchases/subscriptions/' . $this->domain, null, 2 ); + + if ( ! $menu_exists ) { + // Remove the submenu auto-created by Core. + $this->hide_submenu_page( 'paid-upgrades.php', 'paid-upgrades.php' ); + } + } + + /** + * Adds Posts menu. + */ + public function add_posts_menu() { + $submenus_to_update = array(); + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'edit.php' ) ) { + $submenus_to_update['edit.php'] = 'https://wordpress.com/posts/' . $this->domain; + $submenus_to_update['post-new.php'] = 'https://wordpress.com/post/' . $this->domain; + } + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'edit-tags.php?taxonomy=category' ) ) { + $submenus_to_update['edit-tags.php?taxonomy=category'] = 'https://wordpress.com/settings/taxonomies/category/' . $this->domain; + } + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'edit-tags.php?taxonomy=post_tag' ) ) { + $submenus_to_update['edit-tags.php?taxonomy=post_tag'] = 'https://wordpress.com/settings/taxonomies/post_tag/' . $this->domain; + } + + $this->update_submenus( 'edit.php', $submenus_to_update ); + } + + /** + * Adds Media menu. + */ + public function add_media_menu() { + if ( self::CLASSIC_VIEW === $this->get_preferred_view( 'upload.php' ) ) { + return; + } + + $this->hide_submenu_page( 'upload.php', 'media-new.php' ); + + $this->update_menu( 'upload.php', 'https://wordpress.com/media/' . $this->domain ); + } + + /** + * Adds Page menu. + */ + public function add_page_menu() { + if ( self::CLASSIC_VIEW === $this->get_preferred_view( 'edit.php?post_type=page' ) ) { + return; + } + + $submenus_to_update = array( + 'edit.php?post_type=page' => 'https://wordpress.com/pages/' . $this->domain, + 'post-new.php?post_type=page' => 'https://wordpress.com/page/' . $this->domain, + ); + $this->update_submenus( 'edit.php?post_type=page', $submenus_to_update ); + } + + /** + * Adds Testimonials menu. + */ + public function add_testimonials_menu() { + $this->add_custom_post_type_menu( 'jetpack-testimonial' ); + } + + /** + * Adds Portfolio menu. + */ + public function add_portfolio_menu() { + $this->add_custom_post_type_menu( 'jetpack-portfolio' ); + } + + /** + * Adds a custom post type menu. + * + * @param string $post_type Custom post type. + */ + public function add_custom_post_type_menu( $post_type ) { + if ( self::CLASSIC_VIEW === $this->get_preferred_view( 'edit.php?post_type=' . $post_type ) ) { + return; + } + + $submenus_to_update = array( + 'edit.php?post_type=' . $post_type => 'https://wordpress.com/types/' . $post_type . '/' . $this->domain, + 'post-new.php?post_type=' . $post_type => 'https://wordpress.com/edit/' . $post_type . '/' . $this->domain, + ); + $this->update_submenus( 'edit.php?post_type=' . $post_type, $submenus_to_update ); + } + + /** + * Adds Comments menu. + */ + public function add_comments_menu() { + if ( self::CLASSIC_VIEW === $this->get_preferred_view( 'edit-comments.php' ) ) { + return; + } + + $this->update_menu( 'edit-comments.php', 'https://wordpress.com/comments/all/' . $this->domain ); + } + + /** + * Adds Appearance menu. + * + * @return string The Customizer URL. + */ + public function add_appearance_menu() { + $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; + $default_customize_slug = add_query_arg( 'return', rawurlencode( remove_query_arg( wp_removable_query_args(), $request_uri ) ), 'customize.php' ); + $default_customize_header_slug_1 = add_query_arg( array( 'autofocus' => array( 'control' => 'header_image' ) ), $default_customize_slug ); + // TODO: Remove WPCom_Theme_Customizer::modify_header_menu_links() and WPcom_Custom_Header::modify_admin_menu_links(). + $default_customize_header_slug_2 = admin_url( 'themes.php?page=custom-header' ); + $default_customize_background_slug_1 = add_query_arg( array( 'autofocus' => array( 'control' => 'background_image' ) ), $default_customize_slug ); + // TODO: Remove Colors_Manager::modify_header_menu_links() and Colors_Manager_Common::modify_header_menu_links(). + $default_customize_background_slug_2 = add_query_arg( array( 'autofocus' => array( 'section' => 'colors_manager_tool' ) ), admin_url( 'customize.php' ) ); + + if ( $this->is_api_request ) { + // In case this is an api request we will have to add the 'return' querystring via JS. + $customize_url = 'customize.php'; + } else { + $customize_url = $default_customize_slug; + } + + $submenus_to_update = array( + $default_customize_slug => $customize_url, + $default_customize_header_slug_1 => add_query_arg( array( 'autofocus' => array( 'control' => 'header_image' ) ), $customize_url ), + $default_customize_header_slug_2 => add_query_arg( array( 'autofocus' => array( 'control' => 'header_image' ) ), $customize_url ), + $default_customize_background_slug_1 => add_query_arg( array( 'autofocus' => array( 'section' => 'colors_manager_tool' ) ), $customize_url ), + $default_customize_background_slug_2 => add_query_arg( array( 'autofocus' => array( 'section' => 'colors_manager_tool' ) ), $customize_url ), + ); + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'themes.php' ) ) { + $submenus_to_update['themes.php'] = 'https://wordpress.com/themes/' . $this->domain; + } + + $this->update_submenus( 'themes.php', $submenus_to_update ); + + $this->hide_submenu_page( 'themes.php', 'custom-header' ); + $this->hide_submenu_page( 'themes.php', 'custom-background' ); + + return $customize_url; + } + + /** + * Adds Plugins menu. + */ + public function add_plugins_menu() { + $this->hide_submenu_page( 'plugins.php', 'plugin-install.php' ); + $this->hide_submenu_page( 'plugins.php', 'plugin-editor.php' ); + + $this->update_menu( 'plugins.php', 'https://wordpress.com/plugins/' . $this->domain ); + } + + /** + * Adds Users menu. + */ + public function add_users_menu() { + $submenus_to_update = array( + 'profile.php' => 'https://wordpress.com/me', + ); + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'users.php' ) ) { + $submenus_to_update['users.php'] = 'https://wordpress.com/people/team/' . $this->domain; + $submenus_to_update['user-new.php'] = 'https://wordpress.com/people/new/' . $this->domain; + } + + $slug = current_user_can( 'list_users' ) ? 'users.php' : 'profile.php'; + $this->update_submenus( $slug, $submenus_to_update ); + add_submenu_page( $slug, esc_attr__( 'Account Settings', 'jetpack' ), __( 'Account Settings', 'jetpack' ), 'read', 'https://wordpress.com/me/account' ); + } + + /** + * Adds Tools menu. + */ + public function add_tools_menu() { + $submenus_to_update = array(); + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'import.php' ) ) { + $submenus_to_update['import.php'] = 'https://wordpress.com/import/' . $this->domain; + } + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'export.php' ) ) { + $submenus_to_update['export.php'] = 'https://wordpress.com/export/' . $this->domain; + } + $this->update_submenus( 'tools.php', $submenus_to_update ); + + $this->hide_submenu_page( 'tools.php', 'tools.php' ); + $this->hide_submenu_page( 'tools.php', 'delete-blog' ); + + add_submenu_page( 'tools.php', esc_attr__( 'Marketing', 'jetpack' ), __( 'Marketing', 'jetpack' ), 'publish_posts', 'https://wordpress.com/marketing/tools/' . $this->domain, null, 0 ); + add_submenu_page( 'tools.php', esc_attr__( 'Earn', 'jetpack' ), __( 'Earn', 'jetpack' ), 'manage_options', 'https://wordpress.com/earn/' . $this->domain, null, 1 ); + } + + /** + * Adds Settings menu. + */ + public function add_options_menu() { + $submenus_to_update = array(); + + $this->hide_submenu_page( 'options-general.php', 'sharing' ); + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'options-general.php' ) ) { + $submenus_to_update['options-general.php'] = 'https://wordpress.com/settings/general/' . $this->domain; + } + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'options-writing.php' ) ) { + $submenus_to_update['options-writing.php'] = 'https://wordpress.com/settings/writing/' . $this->domain; + } + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'options-discussion.php' ) ) { + $submenus_to_update['options-discussion.php'] = 'https://wordpress.com/settings/discussion/' . $this->domain; + } + + $this->update_submenus( 'options-general.php', $submenus_to_update ); + + add_submenu_page( 'options-general.php', esc_attr__( 'Performance', 'jetpack' ), __( 'Performance', 'jetpack' ), 'manage_options', 'https://wordpress.com/settings/performance/' . $this->domain, null, 1 ); + } + + /** + * Adds Jetpack menu. + */ + public function add_jetpack_menu() { + $this->add_admin_menu_separator( 50, 'manage_options' ); + + // TODO: Replace with proper SVG data url. + $icon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 40 40' %3E%3Cpath fill='%23a0a5aa' d='M20 0c11.046 0 20 8.954 20 20s-8.954 20-20 20S0 31.046 0 20 8.954 0 20 0zm11 17H21v19l10-19zM19 4L9 23h10V4z'/%3E%3C/svg%3E"; + + $is_menu_updated = $this->update_menu( 'jetpack', null, null, null, $icon, 51 ); + if ( ! $is_menu_updated ) { + add_menu_page( esc_attr__( 'Jetpack', 'jetpack' ), __( 'Jetpack', 'jetpack' ), 'manage_options', 'jetpack', null, $icon, 51 ); + } + + add_submenu_page( 'jetpack', esc_attr__( 'Activity Log', 'jetpack' ), __( 'Activity Log', 'jetpack' ), 'manage_options', 'https://wordpress.com/activity-log/' . $this->domain, null, 2 ); + add_submenu_page( 'jetpack', esc_attr__( 'Backup', 'jetpack' ), __( 'Backup', 'jetpack' ), 'manage_options', 'https://wordpress.com/backup/' . $this->domain, null, 3 ); + /* translators: Jetpack sidebar menu item. */ + add_submenu_page( 'jetpack', esc_attr__( 'Search', 'jetpack' ), __( 'Search', 'jetpack' ), 'manage_options', 'https://wordpress.com/jetpack-search/' . $this->domain, null, 4 ); + + $this->hide_submenu_page( 'jetpack', 'jetpack#/settings' ); + $this->hide_submenu_page( 'jetpack', 'stats' ); + $this->hide_submenu_page( 'jetpack', esc_url( Redirect::get_url( 'calypso-backups' ) ) ); + $this->hide_submenu_page( 'jetpack', esc_url( Redirect::get_url( 'calypso-scanner' ) ) ); + + if ( ! $is_menu_updated ) { + // Remove the submenu auto-created by Core just to be sure that there no issues on non-admin roles. + remove_submenu_page( 'jetpack', 'jetpack' ); + } + } + + /** + * Update Site Editor menu item's link and position. + */ + public function add_gutenberg_menus() { + if ( self::CLASSIC_VIEW === $this->get_preferred_view( 'admin.php?page=gutenberg-edit-site' ) ) { + return; + } + + $this->update_menu( 'gutenberg-edit-site', 'https://wordpress.com/site-editor/' . $this->domain, null, null, null, 59 ); + + // Gutenberg 11.9 moves the Site Editor to an Appearance submenu as Editor. + $submenus_to_update = array( + 'gutenberg-edit-site' => 'https://wordpress.com/site-editor/' . $this->domain, + ); + $this->update_submenus( 'themes.php', $submenus_to_update ); + // Gutenberg 11.9 adds an redundant site editor entry point that requires some calypso work + // before it can be exposed. Note, there are also already discussions to remove this excess + // item in Gutenberg. + $this->hide_submenu_page( 'themes.php', 'gutenberg-edit-site&styles=open' ); + } + + /** + * Add the calypso /woocommerce-installation/ menu item. + * + * @param array $current_plan The site's plan if they have one. This is passed from WPcom_Admin_Menu to prevent + * redundant database queries. + */ + public function add_woocommerce_installation_menu( $current_plan = null ) { + /** + * Whether to show the WordPress.com WooCommerce Installation menu. + * + * @use add_filter( 'jetpack_show_wpcom_woocommerce_installation_menu', '__return_true' ); + * @module masterbar + * @since 10.3.0 + * @param bool $jetpack_show_wpcom_woocommerce_installation_menu Load the WordPress.com WooCommerce Installation menu item. Default to false. + * @param array $current_plan Data about the current site's plan. + */ + if ( apply_filters( 'jetpack_show_wpcom_woocommerce_installation_menu', false, $current_plan ) ) { + $this->add_admin_menu_separator( 54, 'activate_plugins' ); + + $icon_url = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiPjxwYXRoIGZpbGw9IiNhMmFhYjIiIGQ9Ik02MTIuMTkyIDQyNi4zMzZjMC02Ljg5Ni0zLjEzNi01MS42LTI4LTUxLjYtMzcuMzYgMC00Ni43MDQgNzIuMjU2LTQ2LjcwNCA4Mi42MjQgMCAzLjQwOCAzLjE1MiA1OC40OTYgMjguMDMyIDU4LjQ5NiAzNC4xOTItLjAzMiA0Ni42NzItNzIuMjg4IDQ2LjY3Mi04OS41MnptMjAyLjE5MiAwYzAtNi44OTYtMy4xNTItNTEuNi0yOC4wMzItNTEuNi0zNy4yOCAwLTQ2LjYwOCA3Mi4yNTYtNDYuNjA4IDgyLjYyNCAwIDMuNDA4IDMuMDcyIDU4LjQ5NiAyNy45NTIgNTguNDk2IDM0LjE5Mi0uMDMyIDQ2LjY4OC03Mi4yODggNDYuNjg4LTg5LjUyek0xNDEuMjk2Ljc2OGMtNjguMjI0IDAtMTIzLjUwNCA1NS40ODgtMTIzLjUwNCAxMjMuOTJ2NjUwLjcyYzAgNjguNDMyIDU1LjI5NiAxMjMuOTIgMTIzLjUwNCAxMjMuOTJoMzM5LjgwOGwxMjMuNTA0IDEyMy45MzZWODk5LjMyOGgyNzguMDQ4YzY4LjIyNCAwIDEyMy41Mi01NS40NzIgMTIzLjUyLTEyMy45MnYtNjUwLjcyYzAtNjguNDMyLTU1LjI5Ni0xMjMuOTItMTIzLjUyLTEyMy45MmgtNzQxLjM2em01MjYuODY0IDQyMi4xNmMwIDU1LjA4OC0zMS4wODggMTU0Ljg4LTEwMi42NCAxNTQuODgtNi4yMDggMC0xOC40OTYtMy42MTYtMjUuNDI0LTYuMDE2LTMyLjUxMi0xMS4xNjgtNTAuMTkyLTQ5LjY5Ni01Mi4zNTItNjYuMjU2IDAgMC0zLjA3Mi0xNy43OTItMy4wNzItNDAuNzUyIDAtMjIuOTkyIDMuMDcyLTQ1LjMyOCAzLjA3Mi00NS4zMjggMTUuNTUyLTc1LjcyOCA0My41NTItMTA2LjczNiA5Ni40NDgtMTA2LjczNiA1OS4wNzItLjAzMiA4My45NjggNTguNTI4IDgzLjk2OCAxMTAuMjA4ek00ODYuNDk2IDMwMi40YzAgMy4zOTItNDMuNTUyIDE0MS4xNjgtNDMuNTUyIDIxMy40MjR2NzUuNzEyYy0yLjU5MiAxMi4wOC00LjE2IDI0LjE0NC0yMS44MjQgMjQuMTQ0LTQ2LjYwOCAwLTg4Ljg4LTE1MS40NzItOTIuMDE2LTE2MS44NC02LjIwOCA2Ljg5Ni02Mi4yNCAxNjEuODQtOTYuNDQ4IDE2MS44NC0yNC44NjQgMC00My41NTItMTEzLjY0OC00Ni42MDgtMTIzLjkzNkMxNzYuNzA0IDQzNi42NzIgMTYwIDMzNC4yMjQgMTYwIDMyNy4zMjhjMC0yMC42NzIgMS4xNTItMzguNzM2IDI2LjA0OC0zOC43MzYgNi4yMDggMCAyMS42IDYuMDY0IDIzLjcxMiAxNy4xNjggMTEuNjQ4IDYyLjAzMiAxNi42ODggMTIwLjUxMiAyOS4xNjggMTg1Ljk2OCAxLjg1NiAyLjkyOCAxLjUwNCA3LjAwOCA0LjU2IDEwLjQzMiAzLjE1Mi0xMC4yODggNjYuOTI4LTE2OC43ODQgOTQuOTYtMTY4Ljc4NCAyMi41NDQgMCAzMC40IDQ0LjU5MiAzMy41MzYgNjEuODI0IDYuMjA4IDIwLjY1NiAxMy4wODggNTUuMjE2IDIyLjQxNiA4Mi43NTIgMC0xMy43NzYgMTIuNDgtMjAzLjEyIDY1LjM5Mi0yMDMuMTIgMTguNTkyLjAzMiAyNi43MDQgNi45MjggMjYuNzA0IDI3LjU2OHpNODcwLjMyIDQyMi45MjhjMCA1NS4wODgtMzEuMDg4IDE1NC44OC0xMDIuNjQgMTU0Ljg4LTYuMTkyIDAtMTguNDQ4LTMuNjE2LTI1LjQyNC02LjAxNi0zMi40MzItMTEuMTY4LTUwLjE3Ni00OS42OTYtNTIuMjg4LTY2LjI1NiAwIDAtMy44ODgtMTcuOTItMy44ODgtNDAuODk2czMuODg4LTQ1LjE4NCAzLjg4OC00NS4xODRjMTUuNTUyLTc1LjcyOCA0My40ODgtMTA2LjczNiA5Ni4zODQtMTA2LjczNiA1OS4xMDQtLjAzMiA4My45NjggNTguNTI4IDgzLjk2OCAxMTAuMjA4eiIvPjwvc3ZnPg=='; + $menu_url = 'https://wordpress.com/woocommerce-installation/' . $this->domain; + + // Only show the menu if the user has the capability to activate_plugins. + add_menu_page( esc_attr__( 'WooCommerce', 'jetpack' ), esc_attr__( 'WooCommerce', 'jetpack' ), 'activate_plugins', $menu_url, null, $icon_url, 55 ); + } + } + + /** + * Prepend a dashboard swithcer to the "Screen Options" box of the current page. + * Callback for the 'screen_settings' filter (available in WP 3.0 and up). + * + * @param string $current The currently added panels in screen options. + * + * @return string The HTML code to append to "Screen Options" + */ + public function register_dashboard_switcher( $current ) { + $menu_mappings = require __DIR__ . '/menu-mappings.php'; + $screen = $this->get_current_screen(); + + // Let's show the switcher only in screens that we have a Calypso mapping to switch to. + if ( ! isset( $menu_mappings[ $screen ] ) ) { + return; + } + + $contents = sprintf( + '<div id="dashboard-switcher"><h5>%s</h5><p class="dashboard-switcher-text">%s</p><a class="button button-primary dashboard-switcher-button" href="%s">%s</a></div>', + __( 'Screen features', 'jetpack' ), + __( 'Currently you are seeing the classic WP-Admin view of this page. Would you like to see the default WordPress.com view?', 'jetpack' ), + add_query_arg( 'preferred-view', 'default' ), + __( 'Use WordPress.com view', 'jetpack' ) + ); + + // Prepend the Dashboard swither to the other custom panels. + $current = $contents . $current; + + return $current; + } +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-atomic-admin-menu.php b/plugins/jetpack/modules/masterbar/admin-menu/class-atomic-admin-menu.php new file mode 100644 index 00000000..b3bd7c64 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-atomic-admin-menu.php @@ -0,0 +1,427 @@ +<?php +/** + * Atomic Admin Menu file. + * + * @package automattic/jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +use Automattic\Jetpack\Connection\Client; +use Jetpack_Plan; + +require_once __DIR__ . '/class-admin-menu.php'; + +/** + * Class Atomic_Admin_Menu. + */ +class Atomic_Admin_Menu extends Admin_Menu { + + /** + * Atomic_Admin_Menu constructor. + */ + protected function __construct() { + parent::__construct(); + + add_action( 'wp_enqueue_scripts', array( $this, 'dequeue_scripts' ), 20 ); + add_action( 'admin_enqueue_scripts', array( $this, 'dequeue_scripts' ), 20 ); + add_action( 'wp_ajax_sidebar_state', array( $this, 'ajax_sidebar_state' ) ); + add_action( 'wp_ajax_jitm_dismiss', array( $this, 'wp_ajax_jitm_dismiss' ) ); + + if ( ! $this->is_api_request ) { + add_filter( 'submenu_file', array( $this, 'override_the_theme_installer' ), 10, 2 ); + } + + add_action( + 'admin_menu', + function () { + remove_action( 'admin_menu', 'gutenberg_menu', 9 ); + }, + 0 + ); + } + + /** + * Dequeues unnecessary scripts. + */ + public function dequeue_scripts() { + wp_dequeue_script( 'a8c_wpcom_masterbar_overrides' ); // Initially loaded in modules/masterbar/masterbar/class-masterbar.php. + } + + /** + * Determines whether the current locale is right-to-left (RTL). + * + * Performs the check against the current locale set on the WordPress.com's account settings. + * See `Masterbar::__construct` in `modules/masterbar/masterbar/class-masterbar.php`. + */ + public function is_rtl() { + return get_user_option( 'jetpack_wpcom_is_rtl' ); + } + + /** + * Create the desired menu output. + */ + public function reregister_menu_items() { + parent::reregister_menu_items(); + + $this->add_my_home_menu(); + $this->add_inbox_menu(); + $this->hide_search_menu_for_calypso(); + + // Not needed outside of wp-admin. + if ( ! $this->is_api_request ) { + $this->add_browse_sites_link(); + $this->add_site_card_menu(); + $nudge = $this->get_upsell_nudge(); + if ( $nudge ) { + parent::add_upsell_nudge( $nudge ); + } + + $this->add_new_site_link(); + } + + $this->add_woocommerce_installation_menu(); + + ksort( $GLOBALS['menu'] ); + } + + /** + * Get the preferred view for the given screen. + * + * @param string $screen Screen identifier. + * @param bool $fallback_global_preference (Optional) Whether the global preference for all screens should be used + * as fallback if there is no specific preference for the given screen. + * Default: true. + * @return string + */ + public function get_preferred_view( $screen, $fallback_global_preference = true ) { + // Export on Atomic sites are always managed on WP Admin. + if ( in_array( $screen, array( 'export.php' ), true ) ) { + return self::CLASSIC_VIEW; + } + + /** + * When Jetpack SSO is disabled, we need to force Calypso because it might create confusion to be redirected to WP-Admin. + * Furthermore, because we don't display the quick switcher, users having an WP-Admin interface by default won't be able to go back to the Calyso version. + */ + if ( ! \Jetpack::is_module_active( 'sso' ) ) { + return self::DEFAULT_VIEW; + } + + return parent::get_preferred_view( $screen, $fallback_global_preference ); + } + + /** + * Adds Plugins menu. + */ + public function add_plugins_menu() { + global $submenu; + if ( + isset( $submenu['plugins.php'] ) + /** + * Whether to enable the marketplace feature entrypoint. + * This filter is specific to WPCOM, that's why there is no + * need to use `jetpack_` prefix. + * + * @use add_filter( 'wpcom_marketplace_enabled', '__return_true' ); + * @module masterbar + * @since 10.3 + * @param bool $wpcom_marketplace_enabled Load the WordPress.com Marketplace feature. Default to false. + */ + && apply_filters( 'wpcom_marketplace_enabled', false ) + ) { + $plugins_submenu = $submenu['plugins.php']; + $slug_to_update = 'plugin-install.php'; + + // Move "Add New" plugin submenu ( `plugin-install.php` ) to the top position. + foreach ( $plugins_submenu as $submenu_key => $submenu_keys ) { + if ( $submenu_keys[2] === $slug_to_update ) { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $submenu['plugins.php'] = array( $submenu_key => $plugins_submenu[ $submenu_key ] ) + $plugins_submenu; + } + } + + $submenus_to_update = array( $slug_to_update => 'https://wordpress.com/plugins/' . $this->domain ); + $this->update_submenus( 'plugins.php', $submenus_to_update ); + } + } + + /** + * Adds the site switcher link if user has more than one site. + */ + public function add_browse_sites_link() { + $site_count = get_user_option( 'wpcom_site_count' ); + if ( ! $site_count || $site_count < 2 ) { + return; + } + + // Add the menu item. + add_menu_page( __( 'site-switcher', 'jetpack' ), __( 'Browse sites', 'jetpack' ), 'read', 'https://wordpress.com/home', null, 'dashicons-arrow-left-alt2', 0 ); + add_filter( 'add_menu_classes', array( $this, 'set_browse_sites_link_class' ) ); + } + + /** + * Adds a custom element class for Site Switcher menu item. + * + * @param array $menu Associative array of administration menu items. + * + * @return array + */ + public function set_browse_sites_link_class( array $menu ) { + foreach ( $menu as $key => $menu_item ) { + if ( 'site-switcher' !== $menu_item[3] ) { + continue; + } + + $menu[ $key ][4] = add_cssclass( 'site-switcher', $menu_item[4] ); + break; + } + + return $menu; + } + + /** + * Adds a link to the menu to create a new site. + */ + public function add_new_site_link() { + $site_count = get_user_option( 'wpcom_site_count' ); + if ( $site_count && $site_count > 1 ) { + return; + } + + $this->add_admin_menu_separator(); + add_menu_page( __( 'Add New Site', 'jetpack' ), __( 'Add New Site', 'jetpack' ), 'read', 'https://wordpress.com/start?ref=calypso-sidebar', null, 'dashicons-plus-alt' ); + } + + /** + * Adds site card component. + */ + public function add_site_card_menu() { + $default = 'data:image/svg+xml,' . rawurlencode( '<svg class="gridicon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Globe</title><rect fill-opacity="0" x="0" width="24" height="24"/><g><path fill="#fff" d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm0 18l2-2 1-1v-2h-2v-1l-1-1H9v3l2 2v1.93c-3.94-.494-7-3.858-7-7.93l1 1h2v-2h2l3-3V6h-2L9 5v-.41C9.927 4.21 10.94 4 12 4s2.073.212 3 .59V6l-1 1v2l1 1 3.13-3.13c.752.897 1.304 1.964 1.606 3.13H18l-2 2v2l1 1h2l.286.286C18.03 18.06 15.24 20 12 20z"/></g></svg>' ); + $icon = get_site_icon_url( 32, $default ); + $blog_name = get_option( 'blogname' ) !== '' ? get_option( 'blogname' ) : $this->domain; + $is_coming_soon = ( function_exists( 'site_is_coming_soon' ) && site_is_coming_soon() ) || (bool) get_option( 'wpcom_public_coming_soon' ); + + $badge = ''; + if ( ( function_exists( 'site_is_private' ) && site_is_private() ) || $is_coming_soon ) { + $badge .= sprintf( + '<span class="site__badge site__badge-private">%s</span>', + $is_coming_soon ? esc_html__( 'Coming Soon', 'jetpack' ) : esc_html__( 'Private', 'jetpack' ) + ); + } + + $site_card = ' +<div class="site__info"> + <div class="site__title">%1$s</div> + <div class="site__domain">%2$s</div> + %3$s +</div>'; + + $site_card = sprintf( + $site_card, + $blog_name, + $this->domain, + $badge + ); + + add_menu_page( 'site-card', $site_card, 'read', get_home_url(), null, $icon, 1 ); + add_filter( 'add_menu_classes', array( $this, 'set_site_card_menu_class' ) ); + } + + /** + * Adds a custom element class and id for Site Card's menu item. + * + * @param array $menu Associative array of administration menu items. + * + * @return array + */ + public function set_site_card_menu_class( array $menu ) { + foreach ( $menu as $key => $menu_item ) { + if ( 'site-card' !== $menu_item[3] ) { + continue; + } + + $classes = ' toplevel_page_site-card'; + + // webclip.png is the default on WoA sites. Anything other than that means we have a custom site icon. + if ( has_site_icon() && 'https://s0.wp.com/i/webclip.png' !== get_site_icon_url( 512 ) ) { + $classes .= ' has-site-icon'; + } + + $menu[ $key ][4] = $menu_item[4] . $classes; + $menu[ $key ][5] = 'toplevel_page_site_card'; + break; + } + + return $menu; + } + + /** + * Returns the first available upsell nudge. + * + * @return array + */ + public function get_upsell_nudge() { + $jitm = \Automattic\Jetpack\JITMS\JITM::get_instance(); + $message_path = 'calypso:sites:sidebar_notice'; + $message = $jitm->get_messages( $message_path, wp_json_encode( array( 'message_path' => $message_path ) ), false ); + + if ( isset( $message[0] ) ) { + $message = $message[0]; + return array( + 'content' => $message->content->message, + 'cta' => $message->CTA->message, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + 'link' => $message->CTA->link, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + 'tracks_impression_event_name' => $message->tracks->display->name, + 'tracks_impression_cta_name' => $message->tracks->display->props->cta_name, + 'tracks_click_event_name' => $message->tracks->click->name, + 'tracks_click_cta_name' => $message->tracks->click->props->cta_name, + 'dismissible' => $message->is_dismissible, + 'feature_class' => $message->feature_class, + 'id' => $message->id, + ); + } + } + + /** + * Adds Stats menu. + */ + public function add_stats_menu() { + $menu_title = __( 'Stats', 'jetpack' ); + + if ( + ! $this->is_api_request && + \Jetpack::is_module_active( 'stats' ) && + function_exists( 'stats_get_image_chart_src' ) + ) { + $img_src = esc_attr( + stats_get_image_chart_src( 'admin-bar-hours-scale-2x', array( 'masterbar' => '' ) ) + ); + $alt = esc_attr__( 'Hourly views', 'jetpack' ); + + $menu_title .= "<img class='sidebar-unified__sparkline' src='$img_src' width='80' height='20' alt='$alt'>"; + } + + add_menu_page( __( 'Stats', 'jetpack' ), $menu_title, 'edit_posts', 'https://wordpress.com/stats/day/' . $this->domain, null, 'dashicons-chart-bar', 3 ); + } + + /** + * Adds Upgrades menu. + * + * @param string $plan The current WPCOM plan of the blog. + */ + public function add_upgrades_menu( $plan = null ) { + $products = Jetpack_Plan::get(); + if ( array_key_exists( 'product_name_short', $products ) ) { + $plan = $products['product_name_short']; + } + parent::add_upgrades_menu( $plan ); + + $last_upgrade_submenu_position = $this->get_submenu_item_count( 'paid-upgrades.php' ); + + add_submenu_page( 'paid-upgrades.php', __( 'Domains', 'jetpack' ), __( 'Domains', 'jetpack' ), 'manage_options', 'https://wordpress.com/domains/manage/' . $this->domain, null, $last_upgrade_submenu_position - 1 ); + + /** + * Whether to show the WordPress.com Emails submenu under the main Upgrades menu. + * + * @use add_filter( 'jetpack_show_wpcom_upgrades_email_menu', '__return_true' ); + * @module masterbar + * + * @since 9.7.0 + * + * @param bool $show_wpcom_upgrades_email_menu Load the WordPress.com Emails submenu item. Default to false. + */ + if ( apply_filters( 'jetpack_show_wpcom_upgrades_email_menu', false ) ) { + add_submenu_page( 'paid-upgrades.php', __( 'Emails', 'jetpack' ), __( 'Emails', 'jetpack' ), 'manage_options', 'https://wordpress.com/email/' . $this->domain, null, $last_upgrade_submenu_position ); + } + } + + /** + * Adds Settings menu. + */ + public function add_options_menu() { + parent::add_options_menu(); + + if ( Jetpack_Plan::supports( 'security-settings' ) ) { + add_submenu_page( + 'options-general.php', + esc_attr__( 'Security', 'jetpack' ), + __( 'Security', 'jetpack' ), + 'manage_options', + 'https://wordpress.com/settings/security/' . $this->domain, + null, + 2 + ); + } + add_submenu_page( 'options-general.php', esc_attr__( 'Hosting Configuration', 'jetpack' ), __( 'Hosting Configuration', 'jetpack' ), 'manage_options', 'https://wordpress.com/hosting-config/' . $this->domain, null, 11 ); + add_submenu_page( 'options-general.php', esc_attr__( 'Jetpack', 'jetpack' ), __( 'Jetpack', 'jetpack' ), 'manage_options', 'https://wordpress.com/settings/jetpack/' . $this->domain, null, 12 ); + + // Page Optimize is active by default on all Atomic sites and registers a Settings > Performance submenu which + // would conflict with our own Settings > Performance that links to Calypso, so we hide it it since the Calypso + // performance settings already have a link to Page Optimize settings page. + $this->hide_submenu_page( 'options-general.php', 'page-optimize' ); + } + + /** + * Override the global submenu_file for theme-install.php page so the WP Admin menu item gets highlighted correctly. + * + * @param string $submenu_file The current pages $submenu_file global variable value. + * @return string | null + */ + public function override_the_theme_installer( $submenu_file ) { + global $pagenow; + + if ( 'themes.php' === $submenu_file && 'theme-install.php' === $pagenow ) { + return null; + } + return $submenu_file; + } + + /** + * Also remove the Gutenberg plugin menu. + */ + public function add_gutenberg_menus() { + // Always remove the Gutenberg menu. + remove_menu_page( 'gutenberg' ); + parent::add_gutenberg_menus(); + } + + /** + * Saves the sidebar state ( expanded / collapsed ) via an ajax request. + */ + public function ajax_sidebar_state() { + $expanded = filter_var( $_REQUEST['expanded'], FILTER_VALIDATE_BOOLEAN ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + Client::wpcom_json_api_request_as_user( + '/me/preferences', + '2', + array( + 'method' => 'POST', + ), + (object) array( 'calypso_preferences' => (object) array( 'sidebarCollapsed' => ! $expanded ) ), + 'wpcom' + ); + + wp_die(); + } + + /** + * Handle ajax requests to dismiss a just-in-time-message + */ + public function wp_ajax_jitm_dismiss() { + check_ajax_referer( 'jitm_dismiss' ); + $jitm = \Automattic\Jetpack\JITMS\JITM::get_instance(); + $jitm->dismiss( $_REQUEST['id'], $_REQUEST['feature_class'] ); + wp_die(); + } + + /** + * Hide Calypso Search menu for Atomic sites. + * + * For simple sites, where search dashboard doesn't exist, we use the Calypso page / menu item. + * For Atomic sites, the admin-menu is originated from the sites and forwarded by WPCOM `public-api`. + * We have search dashboard for Atomic/JP sites, so we need to hide the duplicated menu item. + */ + public function hide_search_menu_for_calypso() { + $this->hide_submenu_page( 'jetpack', 'https://wordpress.com/jetpack-search/' . $this->domain ); + } +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-base-admin-menu.php b/plugins/jetpack/modules/masterbar/admin-menu/class-base-admin-menu.php new file mode 100644 index 00000000..5f3b1f51 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-base-admin-menu.php @@ -0,0 +1,694 @@ +<?php +/** + * Base Admin Menu file. + * + * @package automattic/jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +use Automattic\Jetpack\Status; + +/** + * Class Base_Admin_Menu + */ +abstract class Base_Admin_Menu { + /** + * Holds class instances. + * + * @var array + */ + protected static $instances; + + /** + * Whether the current request is a REST API request. + * + * @var bool + */ + protected $is_api_request = false; + + /** + * Domain of the current site. + * + * @var string + */ + protected $domain; + + /** + * The CSS classes used to hide the submenu items in navigation. + * + * @var string + */ + const HIDE_CSS_CLASS = 'hide-if-js'; + + /** + * Identifier denoting that the default WordPress.com view should be used for a certain screen. + * + * @var string + */ + const DEFAULT_VIEW = 'default'; + + /** + * Identifier denoting that the classic WP Admin view should be used for a certain screen. + * + * @var string + */ + const CLASSIC_VIEW = 'classic'; + + /** + * Identifier denoting no preferred view has been set for a certain screen. + * + * @var string + */ + const UNKNOWN_VIEW = 'unknown'; + + /** + * Base_Admin_Menu constructor. + */ + protected function __construct() { + $this->is_api_request = defined( 'REST_REQUEST' ) && REST_REQUEST || 0 === strpos( $_SERVER['REQUEST_URI'], '/?rest_route=%2Fwpcom%2Fv2%2Fadmin-menu' ); + $this->domain = ( new Status() )->get_site_suffix(); + + add_action( 'admin_menu', array( $this, 'reregister_menu_items' ), 99998 ); + add_action( 'admin_menu', array( $this, 'hide_parent_of_hidden_submenus' ), 99999 ); + + if ( ! $this->is_api_request ) { + add_filter( 'admin_menu', array( $this, 'override_svg_icons' ), 99999 ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ), 11 ); + add_action( 'admin_head', array( $this, 'set_site_icon_inline_styles' ) ); + add_filter( 'screen_settings', array( $this, 'register_dashboard_switcher' ), 99999 ); + add_action( 'admin_menu', array( $this, 'handle_preferred_view' ), 99997 ); + add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) ); + } + } + + /** + * Returns class instance. + * + * @return Admin_Menu + */ + public static function get_instance() { + $class = static::class; + + if ( empty( static::$instances[ $class ] ) ) { + static::$instances[ $class ] = new $class(); + } + + return static::$instances[ $class ]; + } + + /** + * Updates the menu data of the given menu slug. + * + * @param string $slug Slug of the menu to update. + * @param string $url New menu URL. + * @param string $title New menu title. + * @param string $cap New menu capability. + * @param string $icon New menu icon. + * @param int $position New menu position. + * @return bool Whether the menu has been updated. + */ + public function update_menu( $slug, $url = null, $title = null, $cap = null, $icon = null, $position = null ) { + global $menu, $submenu; + + $menu_item = null; + $menu_position = null; + + foreach ( $menu as $i => $item ) { + if ( $slug === $item[2] ) { + $menu_item = $item; + $menu_position = $i; + break; + } + } + + if ( ! $menu_item ) { + return false; + } + + if ( $title ) { + $menu_item[0] = $title; + $menu_item[3] = esc_attr( $title ); + } + + if ( $cap ) { + $menu_item[1] = $cap; + } + + // Change parent slug only if there are no submenus (the slug of the 1st submenu will be used if there are submenus). + if ( $url ) { + $this->hide_submenu_page( $slug, $slug ); + + if ( ! isset( $submenu[ $slug ] ) || ! $this->has_visible_items( $submenu[ $slug ] ) ) { + $menu_item[2] = $url; + } + } + + if ( $icon ) { + $menu_item[4] = 'menu-top'; + $menu_item[6] = $icon; + } + + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + unset( $menu[ $menu_position ] ); + if ( $position ) { + $menu_position = $position; + } + $this->set_menu_item( $menu_item, $menu_position ); + + // Only add submenu when there are other submenu items. + if ( $url && isset( $submenu[ $slug ] ) && $this->has_visible_items( $submenu[ $slug ] ) ) { + add_submenu_page( $slug, $menu_item[3], $menu_item[0], $menu_item[1], $url, null, 0 ); + } + + return true; + } + + /** + * Updates the submenus of the given menu slug. + * + * It hides the menu by adding the `hide-if-js` css class and duplicates the submenu with the new slug. + * + * @param string $slug Menu slug. + * @param array $submenus_to_update Array of new submenu slugs. + */ + public function update_submenus( $slug, $submenus_to_update ) { + global $submenu; + + if ( ! isset( $submenu[ $slug ] ) ) { + return; + } + + // This is needed for cases when the submenus to update have the same new slug. + $submenus_to_update = array_filter( + $submenus_to_update, + static function ( $item, $old_slug ) { + return $item !== $old_slug; + }, + ARRAY_FILTER_USE_BOTH + ); + + /** + * Iterate over all submenu items and add the hide the submenus with CSS classes. + * This is done separately of the second foreach because the position of the submenu might change. + */ + foreach ( $submenu[ $slug ] as $index => $item ) { + if ( ! array_key_exists( $item[2], $submenus_to_update ) ) { + continue; + } + + $this->hide_submenu_element( $index, $slug, $item ); + } + + $submenu_items = array_values( $submenu[ $slug ] ); + + /** + * Iterate again over the submenu array. We need a copy of the array because add_submenu_page will add new elements + * to submenu array that might cause an infinite loop. + */ + foreach ( $submenu_items as $i => $submenu_item ) { + if ( ! array_key_exists( $submenu_item[2], $submenus_to_update ) ) { + continue; + } + + add_submenu_page( + $slug, + isset( $submenu_item[3] ) ? $submenu_item[3] : '', + isset( $submenu_item[0] ) ? $submenu_item[0] : '', + isset( $submenu_item[1] ) ? $submenu_item[1] : 'read', + $submenus_to_update[ $submenu_item[2] ], + '', + 0 === $i ? 0 : $i + 1 + ); + } + } + + /** + * Adds a menu separator. + * + * @param int $position The position in the menu order this item should appear. + * @param string $cap Optional. The capability required for this menu to be displayed to the user. + * Default: 'read'. + */ + public function add_admin_menu_separator( $position = null, $cap = 'read' ) { + $menu_item = array( + '', // Menu title (ignored). + $cap, // Required capability. + wp_unique_id( 'separator-custom-' ), // URL or file (ignored, but must be unique). + '', // Page title (ignored). + 'wp-menu-separator', // CSS class. Identifies this item as a separator. + ); + + $this->set_menu_item( $menu_item, $position ); + } + + /** + * Enqueues scripts and styles. + */ + public function enqueue_scripts() { + $is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM; + + if ( $this->is_rtl() ) { + if ( $is_wpcom ) { + $css_path = 'rtl/admin-menu-rtl.css'; + } else { + $css_path = 'admin-menu-rtl.css'; + } + } else { + $css_path = 'admin-menu.css'; + } + + wp_enqueue_style( + 'jetpack-admin-menu', + plugins_url( $css_path, __FILE__ ), + array(), + JETPACK__VERSION + ); + + wp_style_add_data( 'jetpack-admin-menu', 'rtl', $this->is_rtl() ); + $this->configure_colors_for_rtl_stylesheets(); + + wp_enqueue_script( + 'jetpack-admin-menu', + plugins_url( 'admin-menu.js', __FILE__ ), + array(), + JETPACK__VERSION, + true + ); + + wp_localize_script( + 'jetpack-admin-menu', + 'jetpackAdminMenu', + array( 'jitmDismissNonce' => wp_create_nonce( 'jitm_dismiss' ) ) + ); + } + + /** + * Mark the core colors stylesheets as RTL depending on the value from the environment. + * This fixes a core issue where the extra RTL data is not added to the colors stylesheet. + * https://core.trac.wordpress.org/ticket/53090 + */ + public function configure_colors_for_rtl_stylesheets() { + wp_style_add_data( 'colors', 'rtl', $this->is_rtl() ); + } + + /** + * Injects inline-styles for site icon for when third-party plugins remove enqueued stylesheets. + * Unable to use wp_add_inline_style as plugins remove styles from all non-standard handles + */ + public function set_site_icon_inline_styles() { + echo '<style> + #adminmenu .toplevel_page_site-card .wp-menu-image, + #adminmenu .toplevel_page_site-card .wp-menu-image img { + height: 32px; + width: 32px; + } + </style>'; + } + + /** + * Hide the submenu page based on slug and return the item that was hidden. + * + * Instead of actually removing the submenu item, a safer approach is to hide it and filter it in the API response. + * In this manner we'll avoid breaking third-party plugins depending on items that no longer exist. + * + * A false|array value is returned to be consistent with remove_submenu_page() function + * + * @param string $menu_slug The parent menu slug. + * @param string $submenu_slug The submenu slug that should be hidden. + * @return false|array + */ + public function hide_submenu_page( $menu_slug, $submenu_slug ) { + global $submenu; + + if ( ! isset( $submenu[ $menu_slug ] ) ) { + return false; + } + + foreach ( $submenu[ $menu_slug ] as $i => $item ) { + if ( $submenu_slug !== $item[2] ) { + continue; + } + + $this->hide_submenu_element( $i, $menu_slug, $item ); + + return $item; + } + + return false; + } + + /** + * Apply the hide-if-js CSS class to a submenu item. + * + * @param int $index The position of a submenu item in the submenu array. + * @param string $parent_slug The parent slug. + * @param array $item The submenu item. + */ + public function hide_submenu_element( $index, $parent_slug, $item ) { + global $submenu; + + $css_classes = empty( $item[4] ) ? self::HIDE_CSS_CLASS : $item[4] . ' ' . self::HIDE_CSS_CLASS; + + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $submenu [ $parent_slug ][ $index ][4] = $css_classes; + } + + /** + * Check if the menu has submenu items visible + * + * @param array $submenu_items The submenu items. + * @return bool + */ + public function has_visible_items( $submenu_items ) { + $visible_items = array_filter( + $submenu_items, + array( $this, 'is_item_visible' ) + ); + + return array() !== $visible_items; + } + + /** + * Return the number of existing submenu items under the supplied parent slug. + * + * @param string $parent_slug The slug of the parent menu. + * @return int The number of submenu items under $parent_slug. + */ + public function get_submenu_item_count( $parent_slug ) { + global $submenu; + + if ( empty( $parent_slug ) || empty( $submenu[ $parent_slug ] ) || ! is_array( $submenu[ $parent_slug ] ) ) { + return 0; + } + + return count( $submenu[ $parent_slug ] ); + } + + /** + * Adds the given menu item in the specified position. + * + * @param array $item The menu item to add. + * @param int $position The position in the menu order this item should appear. + */ + public function set_menu_item( $item, $position = null ) { + global $menu; + + // Handle position (avoids overwriting menu items already populated in the given position). + // Inspired by https://core.trac.wordpress.org/browser/trunk/src/wp-admin/menu.php?rev=49837#L160. + if ( null === $position ) { + $menu[] = $item; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + } elseif ( isset( $menu[ "$position" ] ) ) { + $position = $position + substr( base_convert( md5( $item[2] . $item[0] ), 16, 10 ), -5 ) * 0.00001; + $menu[ "$position" ] = $item; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + } else { + $menu[ $position ] = $item; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + } + } + + /** + * Determines whether the current locale is right-to-left (RTL). + */ + public function is_rtl() { + return is_rtl(); + } + + /** + * Checks for any SVG icons in the menu, and overrides things so that + * we can display the icon in the correct colour for the theme. + */ + public function override_svg_icons() { + global $menu; + + $svg_items = array(); + foreach ( $menu as $idx => $menu_item ) { + // Menu items that don't have icons, for example separators, have less than 7 + // elements, partly because the 7th is the icon. So, if we have less than 7, + // let's skip it. + if ( count( $menu_item ) < 7 ) { + continue; + } + + // If the hookname contain a URL than sanitize it by replacing invalid characters. + if ( false !== strpos( $menu_item[5], '://' ) ) { + $menu_item[5] = preg_replace( '![:/.]+!', '_', $menu_item[5] ); + } + + if ( 0 === strpos( $menu_item[6], 'data:image/svg+xml' ) && 'site-card' !== $menu_item[3] ) { + $svg_items[] = array( + 'icon' => $menu_item[6], + 'id' => $menu_item[5], + ); + $menu_item[4] .= ' menu-svg-icon'; + $menu_item[6] = 'none'; + } + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $menu[ $idx ] = $menu_item; + } + if ( count( $svg_items ) > 0 ) { + $styles = '.menu-svg-icon .wp-menu-image { background-repeat: no-repeat; background-position: center center } '; + foreach ( $svg_items as $svg_item ) { + $styles .= sprintf( '#%s .wp-menu-image { background-image: url( "%s" ) }', $svg_item['id'], $svg_item['icon'] ); + } + $styles .= '@supports ( mask-image: none ) or ( -webkit-mask-image: none ) { '; + $styles .= '.menu-svg-icon .wp-menu-image { background-image: none; } '; + $styles .= '.menu-svg-icon .wp-menu-image::before { background-color: currentColor; '; + $styles .= 'mask-size: contain; mask-position: center center; mask-repeat: no-repeat; '; + $styles .= '-webkit-mask-size: contain; -webkit-mask-position: center center; -webkit-mask-repeat: no-repeat; content:"" } '; + foreach ( $svg_items as $svg_item ) { + $styles .= sprintf( + '#%s .wp-menu-image { background-image: none; } #%s .wp-menu-image::before{ mask-image: url( "%s" ); -webkit-mask-image: url( "%s" ) }', + $svg_item['id'], + $svg_item['id'], + $svg_item['icon'], + $svg_item['icon'] + ); + } + $styles .= '}'; + + wp_register_style( 'svg-menu-overrides', false, array(), '20210331' ); + wp_enqueue_style( 'svg-menu-overrides' ); + wp_add_inline_style( 'svg-menu-overrides', $styles ); + } + } + + /** + * Hide menus that are unauthorized and don't have visible submenus and cases when the menu has the same slug + * as the first submenu item. + * + * This must be done at the end of menu and submenu manipulation in order to avoid performing this check each time + * the submenus are altered. + */ + public function hide_parent_of_hidden_submenus() { + global $menu, $submenu; + + $this->sort_hidden_submenus(); + + foreach ( $menu as $menu_index => $menu_item ) { + $has_submenus = isset( $submenu[ $menu_item[2] ] ); + + // Skip if the menu doesn't have submenus. + if ( ! $has_submenus ) { + continue; + } + + // If the first submenu item is hidden then we should also hide the parent. + // Since the submenus are ordered by self::HIDE_CSS_CLASS (hidden submenus should be at the end of the array), + // we can say that if the first submenu is hidden then we should also hide the menu. + $first_submenu_item = array_values( $submenu[ $menu_item[2] ] )[0]; + $is_first_submenu_visible = $this->is_item_visible( $first_submenu_item ); + + // if the user does not have access to the menu and the first submenu is hidden, then hide the menu. + if ( ! current_user_can( $menu_item[1] ) && ! $is_first_submenu_visible ) { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $menu[ $menu_index ][4] = self::HIDE_CSS_CLASS; + } + + // if the menu has the same slug as the first submenu then hide the submenu. + if ( $menu_item[2] === $first_submenu_item[2] && ! $is_first_submenu_visible ) { + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $menu[ $menu_index ][4] = self::HIDE_CSS_CLASS; + } + } + } + + /** + * Sort the hidden submenus by moving them at the end of the array in order to avoid WP using them as default URLs. + * + * This operation has to be done at the end of submenu manipulation in order to guarantee that the hidden submenus + * are at the end of the array. + */ + public function sort_hidden_submenus() { + global $submenu; + + foreach ( $submenu as $menu_slug => $submenu_items ) { + foreach ( $submenu_items as $submenu_index => $submenu_item ) { + if ( $this->is_item_visible( $submenu_item ) ) { + continue; + } + + unset( $submenu[ $menu_slug ][ $submenu_index ] ); + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $submenu[ $menu_slug ][] = $submenu_item; + } + } + } + + /** + * Check if the given item is visible or not in the admin menu. + * + * @param array $item A menu or submenu array. + */ + public function is_item_visible( $item ) { + return ! isset( $item[4] ) || false === strpos( $item[4], self::HIDE_CSS_CLASS ); + } + + /** + * Sets the given view as preferred for the givens screen. + * + * @param string $screen Screen identifier. + * @param string $view Preferred view. + */ + public function set_preferred_view( $screen, $view ) { + $preferred_views = $this->get_preferred_views(); + $preferred_views[ $screen ] = $view; + update_user_option( get_current_user_id(), 'jetpack_admin_menu_preferred_views', $preferred_views ); + } + + /** + * Get the preferred views for all screens. + * + * @return array + */ + public function get_preferred_views() { + $preferred_views = get_user_option( 'jetpack_admin_menu_preferred_views' ); + + if ( ! $preferred_views ) { + return array(); + } + + return $preferred_views; + } + + /** + * Get the preferred view for the given screen. + * + * @param string $screen Screen identifier. + * @param bool $fallback_global_preference (Optional) Whether the global preference for all screens should be used + * as fallback if there is no specific preference for the given screen. + * Default: true. + * @return string + */ + public function get_preferred_view( $screen, $fallback_global_preference = true ) { + $preferred_views = $this->get_preferred_views(); + + if ( ! isset( $preferred_views[ $screen ] ) ) { + if ( ! $fallback_global_preference ) { + return self::UNKNOWN_VIEW; + } + return $this->should_link_to_wp_admin() ? self::CLASSIC_VIEW : self::DEFAULT_VIEW; + } + + return $preferred_views[ $screen ]; + } + + /** + * Gets the identifier of the current screen. + * + * @return string + */ + public function get_current_screen() { + // phpcs:disable WordPress.Security.NonceVerification + global $pagenow; + $screen = isset( $_REQUEST['screen'] ) ? $_REQUEST['screen'] : $pagenow; + if ( isset( $_GET['post_type'] ) ) { + $screen = add_query_arg( 'post_type', $_GET['post_type'], $screen ); + } + if ( isset( $_GET['taxonomy'] ) ) { + $screen = add_query_arg( 'taxonomy', $_GET['taxonomy'], $screen ); + } + if ( isset( $_GET['page'] ) ) { + $screen = add_query_arg( 'page', $_GET['page'], $screen ); + } + return sanitize_text_field( wp_unslash( $screen ) ); + // phpcs:enable WordPress.Security.NonceVerification + } + + /** + * Stores the preferred view for the current screen. + */ + public function handle_preferred_view() { + // phpcs:disable WordPress.Security.NonceVerification + if ( ! isset( $_GET['preferred-view'] ) ) { + return; + } + + // phpcs:disable WordPress.Security.NonceVerification + $preferred_view = $_GET['preferred-view']; + + if ( ! in_array( $preferred_view, array( self::DEFAULT_VIEW, self::CLASSIC_VIEW ), true ) ) { + return; + } + + $current_screen = $this->get_current_screen(); + + $this->set_preferred_view( $current_screen, $preferred_view ); + + /** + * Dashboard Quick switcher action triggered when a user switches to a different view. + * + * @module masterbar + * + * @since 9.9.1 + * + * @param string The current screen of the user. + * @param string The preferred view the user selected. + */ + \do_action( 'jetpack_dashboard_switcher_changed_view', $current_screen, $preferred_view ); + + if ( self::DEFAULT_VIEW === $preferred_view ) { + // Redirect to default view if that's the newly preferred view. + $menu_mappings = require __DIR__ . '/menu-mappings.php'; + if ( isset( $menu_mappings[ $current_screen ] ) ) { + // Using `wp_redirect` intentionally because we're redirecting to Calypso. + wp_redirect( $menu_mappings[ $current_screen ] . $this->domain ); // phpcs:ignore WordPress.Security.SafeRedirect + exit; + } + } elseif ( self::CLASSIC_VIEW === $preferred_view ) { + // Removes the `preferred-view` param from the URL to avoid issues with + // screens that don't expect this param to be present in the URL. + wp_safe_redirect( remove_query_arg( 'preferred-view' ) ); + exit; + } + // phpcs:enable WordPress.Security.NonceVerification + } + + /** + * Adds the necessary CSS class to the admin body class. + * + * @param string $admin_body_classes Contains all the admin body classes. + * + * @return string + */ + public function admin_body_class( $admin_body_classes ) { + return " is-nav-unification $admin_body_classes "; + } + + /** + * Whether to use wp-admin pages rather than Calypso. + * + * Options: + * false - Calypso (Default). + * true - wp-admin. + * + * @return bool + */ + public function should_link_to_wp_admin() { + return get_user_option( 'jetpack_admin_menu_link_destination' ); + } + + /** + * Create the desired menu output. + */ + abstract public function reregister_menu_items(); +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-dashboard-switcher-tracking.php b/plugins/jetpack/modules/masterbar/admin-menu/class-dashboard-switcher-tracking.php new file mode 100644 index 00000000..b3fe7cd8 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-dashboard-switcher-tracking.php @@ -0,0 +1,175 @@ +<?php +/** + * Quick switcher tracking file. + * + * @package automattic/jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +use Automattic\Jetpack\Status\Host; +use Automattic\Jetpack\Terms_Of_Service; +use Automattic\Jetpack\Tracking; +use Jetpack_Plan; + +/** + * Class Dashboard_Switcher_Tracking + */ +class Dashboard_Switcher_Tracking { + + /** + * Jetpack Tracking library will prefix the event name with "jetpack_*" automatically. + */ + const JETPACK_EVENT_NAME = 'dashboard_quick_switch_link_clicked'; + + const WPCOM_EVENT_NAME = 'wpcom_dashboard_quick_switch_link_clicked'; + /** + * Jetpack tracking object. + * + * @var Tracking + */ + private $tracking; + + /** + * Current site plan. + * + * @var string + */ + private $plan; + + /** + * The wpcom_tracks wrapper function. + * + * @var callable + */ + private $wpcom_tracking; + + /** + * Dashboard_Switcher_Tracking constructor. + * + * @param Tracking $tracking Jetpack tracking object. + * @param callable $wpcom_tracking A wrapper over wpcom event record. + * @param string $plan The current site plan. + */ + public function __construct( Tracking $tracking, callable $wpcom_tracking, $plan ) { + $this->tracking = $tracking; + $this->plan = $plan; + $this->wpcom_tracking = $wpcom_tracking; + } + + /** + * Create an event for the Quick switcher when the user changes it's preferred view. + * + * @param string $screen The screen page. + * @param string $view The new preferred view. + */ + public function record_switch_event( $screen, $view ) { + $event_props = array( + 'current_page' => $screen, + 'destination' => $view, + 'plan' => $this->plan, + ); + + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + $event_props['blog_id'] = get_current_blog_id(); + + /** + * Callable injected in the constructor with the static::wpcom_tracks_record_event() static method. + * + * @see wpcom_tracks_record_event A static method from this class that executes the actual WPCOM event record. + */ + $wpcom_tracking = $this->wpcom_tracking; + $wpcom_tracking( $event_props ); + } else { + $this->record_jetpack_event( $event_props ); + } + } + + /** + * Get the current site plan or 'N/A' when we cannot determine site's plan. + * + * @todo: This method can be reused as a wrapper over WPCOM and Atomic as way to get site's current plan (display name). + * + * @return string + */ + public static function get_plan() { + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + if ( class_exists( '\WPCOM_Store_API' ) ) { + // @todo: Maybe introduce a wrapper for this since we are duplicating it from WPCOM_Admin_Menu:253 + $products = \WPCOM_Store_API::get_current_plan( \get_current_blog_id() ); + if ( array_key_exists( 'product_name_short', $products ) ) { + return $products['product_name_short']; + } + } + + return 'N/A'; // maybe we should return free or null? At the moment it's safe to return 'N/A' since we use it only for passing it to the event. + } + + // @todo: Maybe introduce a helper for this since we are duplicating it from Atomic_Admin_Menu:240 + $products = Jetpack_Plan::get(); + if ( array_key_exists( 'product_name_short', $products ) ) { + return $products['product_name_short']; + } + + return 'N/A'; // maybe we should return free or null? At the moment we use it for passing it to the event. + } + + /** + * Record the event with Jetpack implementation. + * + * For Atomic sites we mark the Jetpack ToS option temporary as read. + * + * @todo Remove the jetpack_options_tos_agreed filter for Atomic sites after the Tracking is properly working for AT sites. + * + * @param array $event_properties The event properties. + */ + private function record_jetpack_event( $event_properties ) { + $woa = ( new Host() )->is_woa_site(); + if ( $woa ) { + add_filter( 'jetpack_options', array( __CLASS__, 'mark_jetpack_tos_as_read' ), 10, 2 ); + } + + $this->tracking->record_user_event( self::JETPACK_EVENT_NAME, $event_properties ); + + if ( $woa ) { + \remove_filter( 'jetpack_options', array( __CLASS__, 'mark_jetpack_tos_as_read' ) ); + } + } + + /** + * Trigger the WPCOM tracks_record_event. + * + * @param array $event_props Event props. + */ + public static function wpcom_tracks_record_event( $event_props ) { + jetpack_require_lib( 'tracks/client' ); + \tracks_record_event( \wp_get_current_user(), self::WPCOM_EVENT_NAME, $event_props ); + } + + /** + * Get the tracking product name for the Tracking library. + * + * The tracking product name is used by the Tracking as a prefix for the event name. + * + * @return string + */ + public static function get_jetpack_tracking_product() { + return ( new Host() )->is_woa_site() ? 'atomic' : 'jetpack'; + } + + /** + * Mark the Jetpack ToS as read for Atomic Sites. + * + * @param mixed $option_value The value of the Jetpack option. + * @param string $option_name The name of the Jetpack option. + * + * @return bool + */ + public static function mark_jetpack_tos_as_read( $option_value, $option_name ) { + if ( Terms_Of_Service::OPTION_NAME === $option_name ) { + return true; + } + + return $option_value; + } +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-domain-only-admin-menu.php b/plugins/jetpack/modules/masterbar/admin-menu/class-domain-only-admin-menu.php new file mode 100644 index 00000000..7f9507a2 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-domain-only-admin-menu.php @@ -0,0 +1,28 @@ +<?php +/** + * Domain-only sites Admin Menu file. + * + * @package automattic/jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +require_once __DIR__ . '/class-base-admin-menu.php'; + +/** + * Class Domain_Only_Admin_Menu. + */ +class Domain_Only_Admin_Menu extends Base_Admin_Menu { + + /** + * Create the desired menu output. + */ + public function reregister_menu_items() { + global $menu, $submenu; + + $menu = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $submenu = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + + add_menu_page( esc_attr__( 'Settings', 'jetpack' ), __( 'Settings', 'jetpack' ), 'manage_options', 'https://wordpress.com/domains/manage/' . $this->domain, null, 'dashicons-admin-settings' ); + } +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-jetpack-admin-menu.php b/plugins/jetpack/modules/masterbar/admin-menu/class-jetpack-admin-menu.php new file mode 100644 index 00000000..c61628b5 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-jetpack-admin-menu.php @@ -0,0 +1,252 @@ +<?php +/** + * Jetpack Admin Menu file. + * + * @package Jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +require_once __DIR__ . '/class-admin-menu.php'; + +/** + * Class Jetpack_Admin_Menu. + */ +class Jetpack_Admin_Menu extends Admin_Menu { + /** + * Determines whether the current locale is right-to-left (RTL). + * + * Performs the check against the current locale set on the WordPress.com's account settings. + * See `Masterbar::__construct` in `modules/masterbar/masterbar/class-masterbar.php`. + */ + public function is_rtl() { + return get_user_option( 'jetpack_wpcom_is_rtl' ); + } + + /** + * Create the desired menu output. + */ + public function reregister_menu_items() { + global $menu, $submenu; + + // Reset menus so there are no third-party plugin items. + $menu = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $submenu = array(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + + parent::reregister_menu_items(); + + $this->add_feedback_menu(); + $this->add_wp_admin_menu(); + + ksort( $GLOBALS['menu'] ); + } + + /** + * Get the preferred view for the given screen. + * + * @param string $screen Screen identifier. + * @param bool $fallback_global_preference (Optional) Whether the global preference for all screens should be used + * as fallback if there is no specific preference for the given screen. + * Default: true. + * @return string + */ + public function get_preferred_view( $screen, $fallback_global_preference = true ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + // Force default views (Calypso) on Jetpack sites since Nav Unification is disabled on WP Admin. + return self::DEFAULT_VIEW; + } + + /** + * Adds Posts menu. + */ + public function add_posts_menu() { + $post = get_post_type_object( 'post' ); + add_menu_page( esc_attr( $post->labels->menu_name ), $post->labels->menu_name, $post->cap->edit_posts, 'https://wordpress.com/posts/' . $this->domain, null, 'dashicons-admin-post' ); + } + + /** + * Adds Media menu. + */ + public function add_media_menu() { + add_menu_page( __( 'Media', 'jetpack' ), __( 'Media', 'jetpack' ), 'upload_files', 'https://wordpress.com/media/' . $this->domain, null, 'dashicons-admin-media' ); + } + + /** + * Adds Page menu. + */ + public function add_page_menu() { + $page = get_post_type_object( 'page' ); + add_menu_page( esc_attr( $page->labels->menu_name ), $page->labels->menu_name, $page->cap->edit_posts, 'https://wordpress.com/pages/' . $this->domain, null, 'dashicons-admin-page' ); + } + + /** + * Adds a custom post type menu. + * + * @param string $post_type Custom post type. + */ + public function add_custom_post_type_menu( $post_type ) { + $ptype_obj = get_post_type_object( $post_type ); + if ( empty( $ptype_obj ) ) { + return; + } + + $menu_slug = 'https://wordpress.com/types/' . $post_type . '/' . $this->domain; + + // Menu icon. + $menu_icon = 'dashicons-admin-post'; + if ( is_string( $ptype_obj->menu_icon ) ) { + // Special handling for data:image/svg+xml and Dashicons. + if ( 0 === strpos( $ptype_obj->menu_icon, 'data:image/svg+xml;base64,' ) || 0 === strpos( $ptype_obj->menu_icon, 'dashicons-' ) ) { + $menu_icon = $ptype_obj->menu_icon; + } else { + $menu_icon = esc_url( $ptype_obj->menu_icon ); + } + } + + add_menu_page( esc_attr( $ptype_obj->labels->menu_name ), $ptype_obj->labels->menu_name, $ptype_obj->cap->edit_posts, $menu_slug, null, $menu_icon ); + } + + /** + * Adds Comments menu. + */ + public function add_comments_menu() { + add_menu_page( esc_attr__( 'Comments', 'jetpack' ), __( 'Comments', 'jetpack' ), 'edit_posts', 'https://wordpress.com/comments/all/' . $this->domain, null, 'dashicons-admin-comments' ); + } + + /** + * Adds Feedback menu. + */ + public function add_feedback_menu() { + $post_type = 'feedback'; + + $ptype_obj = get_post_type_object( $post_type ); + if ( empty( $ptype_obj ) ) { + return; + } + + $slug = 'edit.php?post_type=' . $post_type; + $name = __( 'Feedback', 'jetpack' ); + $capability = $ptype_obj->cap->edit_posts; + $icon = $ptype_obj->menu_icon; + $position = 45; // Before Jetpack. + + add_menu_page( esc_attr( $name ), $name, $capability, $slug, null, $icon, $position ); + } + + /** + * Adds Jetpack menu. + */ + public function add_jetpack_menu() { + parent::add_jetpack_menu(); + + // Place "Scan" submenu after Backup. + $position = 0; + global $submenu; + foreach ( $submenu['jetpack'] as $submenu_item ) { + $position ++; + if ( __( 'Backup', 'jetpack' ) === $submenu_item[3] ) { + break; + } + } + add_submenu_page( 'jetpack', esc_attr__( 'Scan', 'jetpack' ), __( 'Scan', 'jetpack' ), 'manage_options', 'https://wordpress.com/scan/' . $this->domain, null, $position ); + } + + /** + * Adds Appearance menu. + * + * @return string The Customizer URL. + */ + public function add_appearance_menu() { + $themes_url = 'https://wordpress.com/themes/' . $this->domain; + // Customize on Jetpack sites is always done on WP Admin (unsupported by Calypso). + $customize_url = 'customize.php'; + + add_menu_page( esc_attr__( 'Appearance', 'jetpack' ), __( 'Appearance', 'jetpack' ), 'switch_themes', $themes_url, null, 'dashicons-admin-appearance', 60 ); + add_submenu_page( $themes_url, esc_attr__( 'Themes', 'jetpack' ), __( 'Themes', 'jetpack' ), 'switch_themes', 'https://wordpress.com/themes/' . $this->domain ); + add_submenu_page( $themes_url, esc_attr__( 'Customize', 'jetpack' ), __( 'Customize', 'jetpack' ), 'customize', $customize_url ); + + return $customize_url; + } + + /** + * Adds Plugins menu. + */ + public function add_plugins_menu() { + add_menu_page( esc_attr__( 'Plugins', 'jetpack' ), __( 'Plugins', 'jetpack' ), 'activate_plugins', 'https://wordpress.com/plugins/' . $this->domain, null, 'dashicons-admin-plugins', 65 ); + } + + /** + * Adds Users menu. + */ + public function add_users_menu() { + if ( current_user_can( 'list_users' ) ) { + add_menu_page( esc_attr__( 'Users', 'jetpack' ), __( 'Users', 'jetpack' ), 'list_users', 'https://wordpress.com/people/team/' . $this->domain, null, 'dashicons-admin-users', 70 ); + } else { + add_menu_page( esc_attr__( 'My Profile', 'jetpack' ), __( 'Profile', 'jetpack' ), 'read', 'https://wordpress.com/me', null, 'dashicons-admin-users', 70 ); + } + } + + /** + * Adds Tools menu. + */ + public function add_tools_menu() { + add_menu_page( esc_attr__( 'Tools', 'jetpack' ), __( 'Tools', 'jetpack' ), 'publish_posts', 'tools.php', null, 'dashicons-admin-tools', 75 ); + + add_submenu_page( 'tools.php', esc_attr__( 'Marketing', 'jetpack' ), __( 'Marketing', 'jetpack' ), 'publish_posts', 'https://wordpress.com/marketing/tools/' . $this->domain ); + add_submenu_page( 'tools.php', esc_attr__( 'Earn', 'jetpack' ), __( 'Earn', 'jetpack' ), 'manage_options', 'https://wordpress.com/earn/' . $this->domain ); + + // Import/Export on Jetpack sites is always handled on WP Admin. + add_submenu_page( 'tools.php', esc_attr__( 'Import', 'jetpack' ), __( 'Import', 'jetpack' ), 'import', 'import.php' ); + add_submenu_page( 'tools.php', esc_attr__( 'Export', 'jetpack' ), __( 'Export', 'jetpack' ), 'export', 'export.php' ); + + // Remove the submenu auto-created by Core. + $this->hide_submenu_page( 'tools.php', 'tools.php' ); + } + + /** + * Adds Settings menu. + */ + public function add_options_menu() { + $slug = 'https://wordpress.com/settings/general/' . $this->domain; + add_menu_page( esc_attr__( 'Settings', 'jetpack' ), __( 'Settings', 'jetpack' ), 'manage_options', $slug, null, 'dashicons-admin-settings', 80 ); + add_submenu_page( $slug, esc_attr__( 'General', 'jetpack' ), __( 'General', 'jetpack' ), 'manage_options', $slug ); + add_submenu_page( $slug, esc_attr__( 'Security', 'jetpack' ), __( 'Security', 'jetpack' ), 'manage_options', 'https://wordpress.com/settings/security/' . $this->domain ); + add_submenu_page( $slug, esc_attr__( 'Performance', 'jetpack' ), __( 'Performance', 'jetpack' ), 'manage_options', 'https://wordpress.com/settings/performance/' . $this->domain ); + add_submenu_page( $slug, esc_attr__( 'Writing', 'jetpack' ), __( 'Writing', 'jetpack' ), 'manage_options', 'https://wordpress.com/settings/writing/' . $this->domain ); + add_submenu_page( $slug, esc_attr__( 'Discussion', 'jetpack' ), __( 'Discussion', 'jetpack' ), 'manage_options', 'https://wordpress.com/settings/discussion/' . $this->domain ); + + $plan_supports_scan = \Jetpack_Plan::supports( 'scan' ); + $products = \Jetpack_Plan::get_products(); + $has_scan_product = false; + + if ( is_array( $products ) ) { + foreach ( $products as $product ) { + if ( strpos( $product['product_slug'], 'jetpack_scan' ) === 0 ) { + $has_scan_product = true; + break; + } + } + } + + $has_scan = $plan_supports_scan || $has_scan_product; + $rewind_state = get_transient( 'jetpack_rewind_state' ); + $has_backup = $rewind_state && in_array( $rewind_state->state, array( 'awaiting_credentials', 'provisioning', 'active' ), true ); + if ( $has_scan || $has_backup ) { + add_submenu_page( $slug, esc_attr__( 'Jetpack', 'jetpack' ), __( 'Jetpack', 'jetpack' ), 'manage_options', 'https://wordpress.com/settings/jetpack/' . $this->domain ); + } + } + + /** + * Adds WP Admin menu. + */ + public function add_wp_admin_menu() { + global $menu; + + // Attempt to get last position. + ksort( $menu ); + end( $menu ); + $position = key( $menu ); + + $this->add_admin_menu_separator( ++ $position ); + add_menu_page( __( 'WP Admin', 'jetpack' ), __( 'WP Admin', 'jetpack' ), 'read', 'index.php', null, 'dashicons-wordpress-alt', $position ); + } +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-p2-admin-menu.php b/plugins/jetpack/modules/masterbar/admin-menu/class-p2-admin-menu.php new file mode 100644 index 00000000..6b45422d --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-p2-admin-menu.php @@ -0,0 +1,199 @@ +<?php +/** + * P2 Admin Menu file. + * + * @package Jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +require_once __DIR__ . '/class-wpcom-admin-menu.php'; + +/** + * Class P2_Admin_Menu. + */ +class P2_Admin_Menu extends WPcom_Admin_Menu { + + /** + * Slug for the "Appearance" menu item. + * + * @var string + */ + private $appearance_slug = 'themes.php'; + + /** + * Slug for the "Jetpack" menu item. + * + * @var string + */ + private $jetpack_slug = 'jetpack'; + + /** + * Slug for the "Upgrades" menu item. + * + * @var string + */ + private $upgrades_slug = 'paid-upgrades.php'; + + /** + * Slug for the "Plugins" menu item. + * + * @var string + */ + private $plugins_slug = 'plugins.php'; + + /** + * Slug for the "Tools" menu item. + * + * @var string + */ + private $tools_slug = 'tools.php'; + + /** + * Whether or not the P2 is a hub. + * + * @var bool + */ + private $is_hub = false; + + /** + * Whether or not the P2 has a paid plan. + * + * @var bool + */ + private $is_paid = false; + + /** + * P2_Admin_Menu constructor. + */ + protected function __construct() { + parent::__construct(); + + if ( + defined( 'IS_WPCOM' ) && IS_WPCOM && + function_exists( 'require_lib' ) + ) { + require_lib( 'wpforteams' ); + + $current_blog_id = get_current_blog_id(); + $this->is_hub = \WPForTeams\Workspace\is_workspace_hub( $current_blog_id ); + $this->is_paid = \WPForTeams\has_p2_plus_plan( \WPForTeams\Workspace\get_hub_blog_id_from_blog_id( $current_blog_id ) ); + } + // Appearance -> AMP. This needs to be called here in the constructor. + // Running it from reregister_menu_items is not early enough. + remove_action( 'admin_menu', 'amp_add_customizer_link' ); + } + + /** + * Create the desired menu output. + */ + public function reregister_menu_items() { + parent::reregister_menu_items(); + + if ( ! $this->is_hub ) { + $this->remove_menus_for_p2_space(); + } else { + $this->remove_menus_for_hub(); + } + + $this->remove_menus_for_all_p2s(); + } + + /** + * Remove menu items that are not applicable for P2 workspace sites. + */ + private function remove_menus_for_p2_space() { + // Non-hub P2s can't have plans at all. + remove_menu_page( $this->upgrades_slug ); + // Jetpack -> Backup. + remove_submenu_page( $this->jetpack_slug, 'https://wordpress.com/backup/' . $this->domain ); + // Appearance -> Themes. + remove_submenu_page( $this->appearance_slug, 'https://wordpress.com/themes/' . $this->domain ); + // Appearance -> Additional CSS. + $customize_custom_css_url = add_query_arg( + array( 'autofocus' => array( 'section' => 'css_nudge' ) ), + 'https://wordpress.com/customize/' . $this->domain + ); + remove_submenu_page( $this->appearance_slug, $customize_custom_css_url ); + } + + /** + * Remove menu items that are not applicable for P2 hubs. + */ + private function remove_menus_for_hub() { + // Hubs can have plans, but not domain and email products. + remove_submenu_page( $this->upgrades_slug, 'https://wordpress.com/domains/manage/' . $this->domain ); + remove_submenu_page( $this->upgrades_slug, 'https://wordpress.com/email/' . $this->domain ); + // Stats. + remove_menu_page( 'https://wordpress.com/stats/day/' . $this->domain ); + // Hide all Jetpack for hubs. + remove_menu_page( $this->jetpack_slug ); + // Hide posts. + remove_menu_page( 'edit.php' ); + // Hide pages. + remove_menu_page( 'edit.php?post_type=page' ); + // Hide media. + remove_menu_page( 'https://wordpress.com/media/' . $this->domain ); + // Hide comments. + remove_menu_page( 'https://wordpress.com/comments/all/' . $this->domain ); + // Hide appearance. + remove_menu_page( $this->appearance_slug ); + // Tools. + remove_menu_page( $this->tools_slug ); + // Hide settings. + remove_submenu_page( 'options-general.php', 'options-reading.php' ); + remove_submenu_page( 'options-general.php', 'options-writing.php' ); + remove_submenu_page( 'options-general.php', 'options-discussion.php' ); + } + + /** + * Remove menu items that are not applicable for all P2s. + */ + private function remove_menus_for_all_p2s() { + // For free sites remove Jetpack menu item. + if ( ! $this->is_paid ) { + remove_menu_page( $this->jetpack_slug ); + } + + // The following menu items are hidden for both hubs and P2 sites. + remove_menu_page( 'link-manager.php' ); + remove_menu_page( 'feedback' ); + remove_menu_page( $this->plugins_slug ); + remove_menu_page( 'https://wordpress.com/plugins/' . $this->domain ); + remove_menu_page( 'https://wordpress.com/inbox/' . $this->domain ); + + remove_submenu_page( $this->tools_slug, 'https://wordpress.com/marketing/tools/' . $this->domain ); + remove_submenu_page( $this->tools_slug, 'https://wordpress.com/earn/' . $this->domain ); + + remove_submenu_page( 'https://wordpress.com/settings/general/' . $this->domain, 'sharing' ); + remove_submenu_page( 'https://wordpress.com/settings/general/' . $this->domain, 'polls&action=options' ); + remove_submenu_page( 'https://wordpress.com/settings/general/' . $this->domain, 'ratings&action=options' ); + remove_submenu_page( + 'options-general.php', + 'https://wordpress.com/hosting-config/' . $this->domain + ); + remove_submenu_page( + 'https://wordpress.com/settings/general/' . $this->domain, + 'https://wordpress.com/marketing/sharing-buttons/' . $this->domain + ); + + /** This action is documented in `wp-content/plugins/p2-editor/classes/p2-editor-admin.php` */ + if ( apply_filters( 'p2tenberg_admin_patterns', apply_filters( 'p2editor_admin_patterns', true ) ) !== true ) { + remove_menu_page( 'edit.php?post_type=p2_pattern' ); + } + remove_submenu_page( + 'edit.php?post_type=p2_pattern', + 'edit-tags.php?taxonomy=post_tag&post_type=p2_pattern' + ); + + // Hide performance settings. + remove_submenu_page( 'options-general.php', 'https://wordpress.com/settings/performance/' . $this->domain ); + } + + /** + * Override, don't add the woocommerce installation menu on any p2s. + * + * @param array|null $current_plan The site's plan. + */ + public function add_woocommerce_installation_menu( $current_plan = null ) {} // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/class-wpcom-admin-menu.php b/plugins/jetpack/modules/masterbar/admin-menu/class-wpcom-admin-menu.php new file mode 100644 index 00000000..12535a4e --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/class-wpcom-admin-menu.php @@ -0,0 +1,461 @@ +<?php +/** + * WP.com Admin Menu file. + * + * @package automattic/jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +use JITM; + +require_once __DIR__ . '/class-admin-menu.php'; + +/** + * Class WPcom_Admin_Menu. + */ +class WPcom_Admin_Menu extends Admin_Menu { + /** + * Holds the current plan, set by get_current_plan(). + * + * @var array + */ + private $current_plan = array(); + + /** + * WPcom_Admin_Menu constructor. + */ + protected function __construct() { + parent::__construct(); + + add_action( 'wp_ajax_sidebar_state', array( $this, 'ajax_sidebar_state' ) ); + add_action( 'wp_ajax_jitm_dismiss', array( $this, 'wp_ajax_jitm_dismiss' ) ); + add_action( 'admin_init', array( $this, 'sync_sidebar_collapsed_state' ) ); + add_action( 'admin_menu', array( $this, 'remove_submenus' ), 140 ); // After hookpress hook at 130. + } + + /** + * Create the desired menu output. + */ + public function reregister_menu_items() { + parent::reregister_menu_items(); + + $this->add_my_home_menu(); + $this->add_inbox_menu(); + + // Not needed outside of wp-admin. + if ( ! $this->is_api_request ) { + $this->add_browse_sites_link(); + $this->add_site_card_menu(); + $nudge = $this->get_upsell_nudge(); + if ( $nudge ) { + parent::add_upsell_nudge( $nudge ); + } + $this->add_new_site_link(); + } + + $this->add_woocommerce_installation_menu( $this->get_current_plan() ); + + ksort( $GLOBALS['menu'] ); + } + + /** + * Get the preferred view for the given screen. + * + * @param string $screen Screen identifier. + * @param bool $fallback_global_preference (Optional) Whether the global preference for all screens should be used + * as fallback if there is no specific preference for the given screen. + * Default: true. + * @return string + */ + public function get_preferred_view( $screen, $fallback_global_preference = true ) { + // When no preferred view has been set for Themes, keep the previous behavior that forced the default view + // regardless of the global preference. + if ( $fallback_global_preference && 'themes.php' === $screen ) { + $preferred_view = parent::get_preferred_view( $screen, false ); + if ( self::UNKNOWN_VIEW === $preferred_view ) { + return self::DEFAULT_VIEW; + } + return $preferred_view; + } + + // Plugins on Simple sites are always managed on Calypso. + if ( 'plugins.php' === $screen ) { + return self::DEFAULT_VIEW; + } + + return parent::get_preferred_view( $screen, $fallback_global_preference ); + } + + /** + * Adds the site switcher link if user has more than one site. + */ + public function add_browse_sites_link() { + if ( count( get_blogs_of_user( get_current_user_id() ) ) < 2 ) { + return; + } + + // Add the menu item. + add_menu_page( __( 'site-switcher', 'jetpack' ), __( 'Browse sites', 'jetpack' ), 'read', 'https://wordpress.com/home', null, 'dashicons-arrow-left-alt2', 0 ); + add_filter( 'add_menu_classes', array( $this, 'set_browse_sites_link_class' ) ); + } + + /** + * Adds a custom element class for Site Switcher menu item. + * + * @param array $menu Associative array of administration menu items. + * @return array + */ + public function set_browse_sites_link_class( array $menu ) { + foreach ( $menu as $key => $menu_item ) { + if ( 'site-switcher' !== $menu_item[3] ) { + continue; + } + + $menu[ $key ][4] = add_cssclass( 'site-switcher', $menu_item[4] ); + break; + } + + return $menu; + } + + /** + * Adds a link to the menu to create a new site. + */ + public function add_new_site_link() { + if ( count( get_blogs_of_user( get_current_user_id() ) ) > 1 ) { + return; + } + + $this->add_admin_menu_separator(); + add_menu_page( __( 'Add New Site', 'jetpack' ), __( 'Add New Site', 'jetpack' ), 'read', 'https://wordpress.com/start?ref=calypso-sidebar', null, 'dashicons-plus-alt' ); + } + + /** + * Adds site card component. + */ + public function add_site_card_menu() { + $default = 'data:image/svg+xml,' . rawurlencode( '<svg class="gridicon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Globe</title><rect fill-opacity="0" x="0" width="24" height="24"/><g><path fill="#fff" d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm0 18l2-2 1-1v-2h-2v-1l-1-1H9v3l2 2v1.93c-3.94-.494-7-3.858-7-7.93l1 1h2v-2h2l3-3V6h-2L9 5v-.41C9.927 4.21 10.94 4 12 4s2.073.212 3 .59V6l-1 1v2l1 1 3.13-3.13c.752.897 1.304 1.964 1.606 3.13H18l-2 2v2l1 1h2l.286.286C18.03 18.06 15.24 20 12 20z"/></g></svg>' ); + $icon = get_site_icon_url( 32, $default ); + $blog_name = get_option( 'blogname' ) !== '' ? get_option( 'blogname' ) : $this->domain; + $is_coming_soon = ( wpcom_is_coming_soon() && is_private_blog() ) || (bool) get_option( 'wpcom_public_coming_soon' ); + + if ( $default === $icon && blavatar_exists( $this->domain ) ) { + $icon = blavatar_url( $this->domain, 'img', 32 ); + } + + $badge = ''; + if ( is_private_blog() || $is_coming_soon ) { + $badge .= sprintf( + '<span class="site__badge site__badge-private">%s</span>', + $is_coming_soon ? esc_html__( 'Coming Soon', 'jetpack' ) : esc_html__( 'Private', 'jetpack' ) + ); + } + + if ( function_exists( 'is_simple_site_redirect' ) && is_simple_site_redirect( $this->domain ) ) { + $badge .= '<span class="site__badge site__badge-redirect">' . esc_html__( 'Redirect', 'jetpack' ) . '</span>'; + } + + if ( ! empty( get_option( 'options' )['is_domain_only'] ) ) { + $badge .= '<span class="site__badge site__badge-domain-only">' . esc_html__( 'Domain', 'jetpack' ) . '</span>'; + } + + $site_card = ' +<div class="site__info"> + <div class="site__title">%1$s</div> + <div class="site__domain">%2$s</div> + %3$s +</div>'; + + $site_card = sprintf( + $site_card, + $blog_name, + $this->domain, + $badge + ); + + add_menu_page( 'site-card', $site_card, 'read', get_home_url(), null, $icon, 1 ); + add_filter( 'add_menu_classes', array( $this, 'set_site_card_menu_class' ) ); + } + + /** + * Adds a custom element class and id for Site Card's menu item. + * + * @param array $menu Associative array of administration menu items. + * @return array + */ + public function set_site_card_menu_class( array $menu ) { + foreach ( $menu as $key => $menu_item ) { + if ( 'site-card' !== $menu_item[3] ) { + continue; + } + + $classes = ' toplevel_page_site-card'; + if ( blavatar_exists( $this->domain ) ) { + $classes .= ' has-site-icon'; + } + + $menu[ $key ][4] = $menu_item[4] . $classes; + $menu[ $key ][5] = 'toplevel_page_site_card'; + break; + } + + return $menu; + } + + /** + * Returns the first available upsell nudge. + * + * @return array + */ + public function get_upsell_nudge() { + require_lib( 'jetpack-jitm/jitm-engine' ); + $jitm_engine = new JITM\Engine(); + + $message_path = 'calypso:sites:sidebar_notice'; + $current_user = wp_get_current_user(); + $user_id = $current_user->ID; + $user_roles = implode( ',', $current_user->roles ); + $query_string = array( + 'message_path' => $message_path, + ); + + // Get the top message only. + $message = $jitm_engine->get_top_messages( $message_path, $user_id, $user_roles, $query_string ); + + if ( isset( $message[0] ) ) { + $message = $message[0]; + return array( + 'content' => $message->content['message'], + 'cta' => $message->CTA['message'], // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + 'link' => $message->CTA['link'], // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + 'tracks_impression_event_name' => $message->tracks['display']['name'], + 'tracks_impression_cta_name' => $message->tracks['display']['props']['cta_name'], + 'tracks_click_event_name' => $message->tracks['click']['name'], + 'tracks_click_cta_name' => $message->tracks['click']['props']['cta_name'], + 'dismissible' => $message->is_dismissible, + 'feature_class' => $message->feature_class, + 'id' => $message->id, + ); + } + } + + /** + * Adds Stats menu. + */ + public function add_stats_menu() { + $menu_title = __( 'Stats', 'jetpack' ); + + if ( ! $this->is_api_request ) { + $menu_title .= sprintf( + '<img class="sidebar-unified__sparkline" width="80" height="20" src="%1$s" alt="%2$s">', + esc_url( site_url( 'wp-includes/charts/admin-bar-hours-scale-2x.php?masterbar=1&s=' . get_current_blog_id() ) ), + esc_attr__( 'Hourly views', 'jetpack' ) + ); + } + + add_menu_page( __( 'Stats', 'jetpack' ), $menu_title, 'edit_posts', 'https://wordpress.com/stats/day/' . $this->domain, null, 'dashicons-chart-bar', 3 ); + } + + /** + * Gets the current plan and stores it in $this->current_plan so the database is only called once per request. + * + * @return array + */ + private function get_current_plan() { + if ( empty( $this->current_plan ) && class_exists( 'WPCOM_Store_API' ) ) { + $this->current_plan = \WPCOM_Store_API::get_current_plan( get_current_blog_id() ); + } + return $this->current_plan; + } + + /** + * Adds Upgrades menu. + * + * @param string $plan The current WPCOM plan of the blog. + */ + public function add_upgrades_menu( $plan = null ) { + $current_plan = $this->get_current_plan(); + if ( ! empty( $current_plan['product_name_short'] ) ) { + $plan = $current_plan['product_name_short']; + } + + parent::add_upgrades_menu( $plan ); + + $last_upgrade_submenu_position = $this->get_submenu_item_count( 'paid-upgrades.php' ); + + add_submenu_page( 'paid-upgrades.php', __( 'Domains', 'jetpack' ), __( 'Domains', 'jetpack' ), 'manage_options', 'https://wordpress.com/domains/manage/' . $this->domain, null, $last_upgrade_submenu_position - 1 ); + + /** This filter is already documented in modules/masterbar/admin-menu/class-atomic-admin-menu.php */ + if ( apply_filters( 'jetpack_show_wpcom_upgrades_email_menu', false ) ) { + add_submenu_page( 'paid-upgrades.php', __( 'Emails', 'jetpack' ), __( 'Emails', 'jetpack' ), 'manage_options', 'https://wordpress.com/email/' . $this->domain, null, $last_upgrade_submenu_position ); + } + } + + /** + * Adds Appearance menu. + */ + public function add_appearance_menu() { + $customize_url = parent::add_appearance_menu(); + + $this->hide_submenu_page( 'themes.php', 'theme-editor.php' ); + + $user_can_customize = current_user_can( 'customize' ); + + if ( $user_can_customize ) { + $customize_custom_css_url = add_query_arg( array( 'autofocus' => array( 'section' => 'jetpack_custom_css' ) ), $customize_url ); + add_submenu_page( 'themes.php', esc_attr__( 'Additional CSS', 'jetpack' ), __( 'Additional CSS', 'jetpack' ), 'customize', esc_url( $customize_custom_css_url ), null, 20 ); + } + } + + /** + * Adds Users menu. + */ + public function add_users_menu() { + $submenus_to_update = array( + 'grofiles-editor' => 'https://wordpress.com/me', + 'grofiles-user-settings' => 'https://wordpress.com/me/account', + ); + + if ( self::DEFAULT_VIEW === $this->get_preferred_view( 'users.php' ) ) { + $submenus_to_update['users.php'] = 'https://wordpress.com/people/team/' . $this->domain; + } + + $slug = current_user_can( 'list_users' ) ? 'users.php' : 'profile.php'; + $this->update_submenus( $slug, $submenus_to_update ); + add_submenu_page( 'users.php', esc_attr__( 'Add New', 'jetpack' ), __( 'Add New', 'jetpack' ), 'promote_users', 'https://wordpress.com/people/new/' . $this->domain, null, 1 ); + } + + /** + * Adds Settings menu. + */ + public function add_options_menu() { + parent::add_options_menu(); + + add_submenu_page( 'options-general.php', esc_attr__( 'Hosting Configuration', 'jetpack' ), __( 'Hosting Configuration', 'jetpack' ), 'manage_options', 'https://wordpress.com/hosting-config/' . $this->domain, null, 10 ); + } + + /** + * Also remove the Gutenberg plugin menu. + */ + public function add_gutenberg_menus() { + // Always remove the Gutenberg menu. + remove_menu_page( 'gutenberg' ); + parent::add_gutenberg_menus(); + } + + /** + * Whether to use wp-admin pages rather than Calypso. + * + * @return bool + */ + public function should_link_to_wp_admin() { + $result = false; // Calypso. + + $user_attribute = get_user_attribute( get_current_user_id(), 'calypso_preferences' ); + if ( ! empty( $user_attribute['linkDestination'] ) ) { + $result = $user_attribute['linkDestination']; + } + + return $result; + } + + /** + * Adds Plugins menu. + */ + public function add_plugins_menu() { + // TODO: Remove wpcom_menu (/wp-content/admin-plugins/wpcom-misc.php). + $count = ''; + if ( ! is_multisite() && current_user_can( 'update_plugins' ) ) { + $update_data = wp_get_update_data(); + $count = sprintf( + '<span class="update-plugins count-%s"><span class="plugin-count">%s</span></span>', + $update_data['counts']['plugins'], + number_format_i18n( $update_data['counts']['plugins'] ) + ); + } + /* translators: %s: Number of pending plugin updates. */ + add_menu_page( esc_attr__( 'Plugins', 'jetpack' ), sprintf( __( 'Plugins %s', 'jetpack' ), $count ), 'activate_plugins', 'plugins.php', null, 'dashicons-admin-plugins', 65 ); + + parent::add_plugins_menu(); + } + + /** + * Saves the sidebar state ( expanded / collapsed ) via an ajax request. + */ + public function ajax_sidebar_state() { + $expanded = filter_var( $_REQUEST['expanded'], FILTER_VALIDATE_BOOLEAN ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $user_id = get_current_user_id(); + $preferences = get_user_attribute( $user_id, 'calypso_preferences' ); + if ( empty( $preferences ) ) { + $preferences = array(); + } + + $value = array_merge( (array) $preferences, array( 'sidebarCollapsed' => ! $expanded ) ); + $value = array_filter( + $value, + function ( $preference ) { + return null !== $preference; + } + ); + + update_user_attribute( $user_id, 'calypso_preferences', $value ); + + die(); + } + + /** + * Handle ajax requests to dismiss a just-in-time-message + */ + public function wp_ajax_jitm_dismiss() { + check_ajax_referer( 'jitm_dismiss' ); + require_lib( 'jetpack-jitm/jitm-engine' ); + JITM\Engine::dismiss( $_REQUEST['id'], $_REQUEST['feature_class'] ); + wp_die(); + } + + /** + * Syncs the sidebar collapsed state from Calypso Preferences. + */ + public function sync_sidebar_collapsed_state() { + $calypso_preferences = get_user_attribute( get_current_user_id(), 'calypso_preferences' ); + + $sidebar_collapsed = isset( $calypso_preferences['sidebarCollapsed'] ) ? $calypso_preferences['sidebarCollapsed'] : false; + set_user_setting( 'mfold', $sidebar_collapsed ? 'f' : 'o' ); + } + + /** + * Removes unwanted submenu items. + * + * These submenus are added across wp-content and should be removed together with these function calls. + */ + public function remove_submenus() { + global $_registered_pages; + + remove_submenu_page( 'index.php', 'akismet-stats' ); + remove_submenu_page( 'index.php', 'my-comments' ); + remove_submenu_page( 'index.php', 'stats' ); + remove_submenu_page( 'index.php', 'subscriptions' ); + + /* @see https://github.com/Automattic/wp-calypso/issues/49210 */ + remove_submenu_page( 'index.php', 'my-blogs' ); + $_registered_pages['admin_page_my-blogs'] = true; // phpcs:ignore + + remove_submenu_page( 'paid-upgrades.php', 'premium-themes' ); + remove_submenu_page( 'paid-upgrades.php', 'domains' ); + remove_submenu_page( 'paid-upgrades.php', 'my-upgrades' ); + remove_submenu_page( 'paid-upgrades.php', 'billing-history' ); + + remove_submenu_page( 'themes.php', 'customize.php?autofocus[panel]=amp_panel&return=' . rawurlencode( admin_url() ) ); + + remove_submenu_page( 'users.php', 'wpcom-invite-users' ); // Wpcom_Invite_Users::action_admin_menu. + + remove_submenu_page( 'options-general.php', 'adcontrol' ); + + // Remove menu item but continue allowing access. + foreach ( array( 'openidserver', 'webhooks' ) as $page_slug ) { + remove_submenu_page( 'options-general.php', $page_slug ); + $_registered_pages[ 'admin_page_' . $page_slug ] = true; // phpcs:ignore + } + } +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/dashicon-set.php b/plugins/jetpack/modules/masterbar/admin-menu/dashicon-set.php new file mode 100644 index 00000000..41695b06 --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/dashicon-set.php @@ -0,0 +1,358 @@ +<?php +/** + * Array that contains all the core dashicons. + * + * @package automattic/jetpack + */ + +return array( + 'dashicons-admin-appearance' => true, + 'dashicons-admin-collapse' => true, + 'dashicons-admin-comments' => true, + 'dashicons-admin-customizer' => true, + 'dashicons-admin-generic' => true, + 'dashicons-admin-home' => true, + 'dashicons-admin-links' => true, + 'dashicons-admin-media' => true, + 'dashicons-admin-multisite' => true, + 'dashicons-admin-network' => true, + 'dashicons-admin-page' => true, + 'dashicons-admin-plugins' => true, + 'dashicons-admin-post' => true, + 'dashicons-admin-settings' => true, + 'dashicons-admin-site-alt' => true, + 'dashicons-admin-site-alt2' => true, + 'dashicons-admin-site-alt3' => true, + 'dashicons-admin-site' => true, + 'dashicons-admin-tools' => true, + 'dashicons-admin-users' => true, + 'dashicons-airplane' => true, + 'dashicons-album' => true, + 'dashicons-align-center' => true, + 'dashicons-align-full-width' => true, + 'dashicons-align-left' => true, + 'dashicons-align-none' => true, + 'dashicons-align-pull-left' => true, + 'dashicons-align-pull-right' => true, + 'dashicons-align-right' => true, + 'dashicons-align-wide' => true, + 'dashicons-amazon' => true, + 'dashicons-analytics' => true, + 'dashicons-archive' => true, + 'dashicons-arrow-down-alt' => true, + 'dashicons-arrow-down-alt2' => true, + 'dashicons-arrow-down' => true, + 'dashicons-arrow-left-alt' => true, + 'dashicons-arrow-left-alt2' => true, + 'dashicons-arrow-left' => true, + 'dashicons-arrow-right-alt' => true, + 'dashicons-arrow-right-alt2' => true, + 'dashicons-arrow-right' => true, + 'dashicons-arrow-up-alt' => true, + 'dashicons-arrow-up-alt2' => true, + 'dashicons-arrow-up-duplicate' => true, + 'dashicons-arrow-up' => true, + 'dashicons-art' => true, + 'dashicons-awards' => true, + 'dashicons-backup' => true, + 'dashicons-bank' => true, + 'dashicons-beer' => true, + 'dashicons-bell' => true, + 'dashicons-block-default' => true, + 'dashicons-book-alt' => true, + 'dashicons-book' => true, + 'dashicons-buddicons-activity' => true, + 'dashicons-buddicons-bbpress-logo' => true, + 'dashicons-buddicons-buddypress-logo' => true, + 'dashicons-buddicons-community' => true, + 'dashicons-buddicons-forums' => true, + 'dashicons-buddicons-friends' => true, + 'dashicons-buddicons-groups' => true, + 'dashicons-buddicons-pm' => true, + 'dashicons-buddicons-replies' => true, + 'dashicons-buddicons-topics' => true, + 'dashicons-buddicons-tracking' => true, + 'dashicons-building' => true, + 'dashicons-businessman' => true, + 'dashicons-businessperson' => true, + 'dashicons-businesswoman' => true, + 'dashicons-button' => true, + 'dashicons-calculator' => true, + 'dashicons-calendar-alt' => true, + 'dashicons-calendar' => true, + 'dashicons-camera-alt' => true, + 'dashicons-camera' => true, + 'dashicons-car' => true, + 'dashicons-carrot' => true, + 'dashicons-cart' => true, + 'dashicons-category' => true, + 'dashicons-chart-area' => true, + 'dashicons-chart-bar' => true, + 'dashicons-chart-line' => true, + 'dashicons-chart-pie' => true, + 'dashicons-clipboard' => true, + 'dashicons-clock' => true, + 'dashicons-cloud-saved' => true, + 'dashicons-cloud-upload' => true, + 'dashicons-cloud' => true, + 'dashicons-code-standards' => true, + 'dashicons-coffee' => true, + 'dashicons-color-picker' => true, + 'dashicons-columns' => true, + 'dashicons-controls-back' => true, + 'dashicons-controls-forward' => true, + 'dashicons-controls-pause' => true, + 'dashicons-controls-play' => true, + 'dashicons-controls-repeat' => true, + 'dashicons-controls-skipback' => true, + 'dashicons-controls-skipforward' => true, + 'dashicons-controls-volumeoff' => true, + 'dashicons-controls-volumeon' => true, + 'dashicons-cover-image' => true, + 'dashicons-dashboard' => true, + 'dashicons-database-add' => true, + 'dashicons-database-export' => true, + 'dashicons-database-import' => true, + 'dashicons-database-remove' => true, + 'dashicons-database-view' => true, + 'dashicons-database' => true, + 'dashicons-desktop' => true, + 'dashicons-dismiss' => true, + 'dashicons-download' => true, + 'dashicons-drumstick' => true, + 'dashicons-edit-large' => true, + 'dashicons-edit-page' => true, + 'dashicons-edit' => true, + 'dashicons-editor-aligncenter' => true, + 'dashicons-editor-alignleft' => true, + 'dashicons-editor-alignright' => true, + 'dashicons-editor-bold' => true, + 'dashicons-editor-break' => true, + 'dashicons-editor-code-duplicate' => true, + 'dashicons-editor-code' => true, + 'dashicons-editor-contract' => true, + 'dashicons-editor-customchar' => true, + 'dashicons-editor-expand' => true, + 'dashicons-editor-help' => true, + 'dashicons-editor-indent' => true, + 'dashicons-editor-insertmore' => true, + 'dashicons-editor-italic' => true, + 'dashicons-editor-justify' => true, + 'dashicons-editor-kitchensink' => true, + 'dashicons-editor-ltr' => true, + 'dashicons-editor-ol-rtl' => true, + 'dashicons-editor-ol' => true, + 'dashicons-editor-outdent' => true, + 'dashicons-editor-paragraph' => true, + 'dashicons-editor-paste-text' => true, + 'dashicons-editor-paste-word' => true, + 'dashicons-editor-quote' => true, + 'dashicons-editor-removeformatting' => true, + 'dashicons-editor-rtl' => true, + 'dashicons-editor-spellcheck' => true, + 'dashicons-editor-strikethrough' => true, + 'dashicons-editor-table' => true, + 'dashicons-editor-textcolor' => true, + 'dashicons-editor-ul' => true, + 'dashicons-editor-underline' => true, + 'dashicons-editor-unlink' => true, + 'dashicons-editor-video' => true, + 'dashicons-ellipsis' => true, + 'dashicons-email-alt' => true, + 'dashicons-email-alt2' => true, + 'dashicons-email' => true, + 'dashicons-embed-audio' => true, + 'dashicons-embed-generic' => true, + 'dashicons-embed-photo' => true, + 'dashicons-embed-post' => true, + 'dashicons-embed-video' => true, + 'dashicons-excerpt-view' => true, + 'dashicons-exit' => true, + 'dashicons-external' => true, + 'dashicons-facebook-alt' => true, + 'dashicons-facebook' => true, + 'dashicons-feedback' => true, + 'dashicons-filter' => true, + 'dashicons-flag' => true, + 'dashicons-food' => true, + 'dashicons-format-aside' => true, + 'dashicons-format-audio' => true, + 'dashicons-format-chat' => true, + 'dashicons-format-gallery' => true, + 'dashicons-format-image' => true, + 'dashicons-format-quote' => true, + 'dashicons-format-status' => true, + 'dashicons-format-video' => true, + 'dashicons-forms' => true, + 'dashicons-fullscreen-alt' => true, + 'dashicons-fullscreen-exit-alt' => true, + 'dashicons-games' => true, + 'dashicons-google' => true, + 'dashicons-googleplus' => true, + 'dashicons-grid-view' => true, + 'dashicons-groups' => true, + 'dashicons-hammer' => true, + 'dashicons-heading' => true, + 'dashicons-heart' => true, + 'dashicons-hidden' => true, + 'dashicons-hourglass' => true, + 'dashicons-html' => true, + 'dashicons-id-alt' => true, + 'dashicons-id' => true, + 'dashicons-image-crop' => true, + 'dashicons-image-filter' => true, + 'dashicons-image-flip-horizontal' => true, + 'dashicons-image-flip-vertical' => true, + 'dashicons-image-rotate-left' => true, + 'dashicons-image-rotate-right' => true, + 'dashicons-image-rotate' => true, + 'dashicons-images-alt' => true, + 'dashicons-images-alt2' => true, + 'dashicons-index-card' => true, + 'dashicons-info-outline' => true, + 'dashicons-info' => true, + 'dashicons-insert-after' => true, + 'dashicons-insert-before' => true, + 'dashicons-insert' => true, + 'dashicons-instagram' => true, + 'dashicons-laptop' => true, + 'dashicons-layout' => true, + 'dashicons-leftright' => true, + 'dashicons-lightbulb' => true, + 'dashicons-linkedin' => true, + 'dashicons-list-view' => true, + 'dashicons-location-alt' => true, + 'dashicons-location' => true, + 'dashicons-lock-duplicate' => true, + 'dashicons-lock' => true, + 'dashicons-marker' => true, + 'dashicons-media-archive' => true, + 'dashicons-media-audio' => true, + 'dashicons-media-code' => true, + 'dashicons-media-default' => true, + 'dashicons-media-document' => true, + 'dashicons-media-interactive' => true, + 'dashicons-media-spreadsheet' => true, + 'dashicons-media-text' => true, + 'dashicons-media-video' => true, + 'dashicons-megaphone' => true, + 'dashicons-menu-alt' => true, + 'dashicons-menu-alt2' => true, + 'dashicons-menu-alt3' => true, + 'dashicons-menu' => true, + 'dashicons-microphone' => true, + 'dashicons-migrate' => true, + 'dashicons-minus' => true, + 'dashicons-money-alt' => true, + 'dashicons-money' => true, + 'dashicons-move' => true, + 'dashicons-nametag' => true, + 'dashicons-networking' => true, + 'dashicons-no-alt' => true, + 'dashicons-no' => true, + 'dashicons-open-folder' => true, + 'dashicons-palmtree' => true, + 'dashicons-paperclip' => true, + 'dashicons-pdf' => true, + 'dashicons-performance' => true, + 'dashicons-pets' => true, + 'dashicons-phone' => true, + 'dashicons-pinterest' => true, + 'dashicons-playlist-audio' => true, + 'dashicons-playlist-video' => true, + 'dashicons-plugins-checked' => true, + 'dashicons-plus-alt' => true, + 'dashicons-plus-alt2' => true, + 'dashicons-plus' => true, + 'dashicons-podio' => true, + 'dashicons-portfolio' => true, + 'dashicons-post-status' => true, + 'dashicons-pressthis' => true, + 'dashicons-printer' => true, + 'dashicons-privacy' => true, + 'dashicons-products' => true, + 'dashicons-randomize' => true, + 'dashicons-reddit' => true, + 'dashicons-redo' => true, + 'dashicons-remove' => true, + 'dashicons-rest-api' => true, + 'dashicons-rss' => true, + 'dashicons-saved' => true, + 'dashicons-schedule' => true, + 'dashicons-screenoptions' => true, + 'dashicons-search' => true, + 'dashicons-share-alt' => true, + 'dashicons-share-alt2' => true, + 'dashicons-share' => true, + 'dashicons-shield-alt' => true, + 'dashicons-shield' => true, + 'dashicons-shortcode' => true, + 'dashicons-slides' => true, + 'dashicons-smartphone' => true, + 'dashicons-smiley' => true, + 'dashicons-sort' => true, + 'dashicons-sos' => true, + 'dashicons-spotify' => true, + 'dashicons-star-empty' => true, + 'dashicons-star-filled' => true, + 'dashicons-star-half' => true, + 'dashicons-sticky' => true, + 'dashicons-store' => true, + 'dashicons-superhero-alt' => true, + 'dashicons-superhero' => true, + 'dashicons-table-col-after' => true, + 'dashicons-table-col-before' => true, + 'dashicons-table-col-delete' => true, + 'dashicons-table-row-after' => true, + 'dashicons-table-row-before' => true, + 'dashicons-table-row-delete' => true, + 'dashicons-tablet' => true, + 'dashicons-tag' => true, + 'dashicons-tagcloud' => true, + 'dashicons-testimonial' => true, + 'dashicons-text-page' => true, + 'dashicons-text' => true, + 'dashicons-thumbs-down' => true, + 'dashicons-thumbs-up' => true, + 'dashicons-tickets-alt' => true, + 'dashicons-tickets' => true, + 'dashicons-tide' => true, + 'dashicons-translation' => true, + 'dashicons-trash' => true, + 'dashicons-twitch' => true, + 'dashicons-twitter-alt' => true, + 'dashicons-twitter' => true, + 'dashicons-undo' => true, + 'dashicons-universal-access-alt' => true, + 'dashicons-universal-access' => true, + 'dashicons-unlock' => true, + 'dashicons-update-alt' => true, + 'dashicons-update' => true, + 'dashicons-upload' => true, + 'dashicons-vault' => true, + 'dashicons-video-alt' => true, + 'dashicons-video-alt2' => true, + 'dashicons-video-alt3' => true, + 'dashicons-visibility' => true, + 'dashicons-warning' => true, + 'dashicons-welcome-add-page' => true, + 'dashicons-welcome-comments' => true, + 'dashicons-welcome-learn-more' => true, + 'dashicons-welcome-view-site' => true, + 'dashicons-welcome-widgets-menus' => true, + 'dashicons-welcome-write-blog' => true, + 'dashicons-whatsapp' => true, + 'dashicons-wordpress-alt' => true, + 'dashicons-wordpress' => true, + 'dashicons-xing' => true, + 'dashicons-yes-alt' => true, + 'dashicons-yes' => true, + 'dashicons-youtube' => true, + 'dashicons-editor-distractionfree' => true, + 'dashicons-exerpt-view' => true, + 'dashicons-format-links' => true, + 'dashicons-format-standard' => true, + 'dashicons-post-trash' => true, + 'dashicons-share1' => true, + 'dashicons-welcome-edit-page' => true, +); diff --git a/plugins/jetpack/modules/masterbar/admin-menu/load.php b/plugins/jetpack/modules/masterbar/admin-menu/load.php new file mode 100644 index 00000000..412819ef --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/load.php @@ -0,0 +1,126 @@ +<?php +/** + * Admin Menu loader. + * + * @package Jetpack + */ + +namespace Automattic\Jetpack\Dashboard_Customizations; + +use Automattic\Jetpack\Status\Host; +use Automattic\Jetpack\Tracking; + +/** + * Checks whether the navigation customizations should be performed for the given class. + * + * @param string $admin_menu_class Class name. + * + * @return bool + */ +function should_customize_nav( $admin_menu_class ) { + // Make sure the class extends the base admin menu class. + if ( ! is_subclass_of( $admin_menu_class, Base_Admin_Menu::class ) ) { + return false; + } + + $is_api_request = defined( 'REST_REQUEST' ) && REST_REQUEST || 0 === strpos( $_SERVER['REQUEST_URI'], '/?rest_route=%2Fwpcom%2Fv2%2Fadmin-menu' ); + + // No nav customizations on WP Admin of Atomic sites when SSO is disabled. + if ( is_a( $admin_menu_class, Atomic_Admin_Menu::class, true ) && ! $is_api_request && ! \Jetpack::is_module_active( 'sso' ) ) { + return false; + } + + // No nav customizations on WP Admin of Jetpack sites. + if ( is_a( $admin_menu_class, Jetpack_Admin_Menu::class, true ) && ! $is_api_request ) { + return false; + } + + return true; +} + +/** + * Gets the name of the class that customizes the admin menu. + * + * @return string Class name. + */ +function get_admin_menu_class() { + // WordPress.com Atomic sites. + if ( ( new Host() )->is_woa_site() ) { + require_once __DIR__ . '/class-atomic-admin-menu.php'; + return Atomic_Admin_Menu::class; + } + + // WordPress.com Simple sites. + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + $blog_id = get_current_blog_id(); + + // Domain-only sites. + $blog_options = get_blog_option( $blog_id, 'options' ); + $is_domain_only = ! empty( $blog_options['is_domain_only'] ); + if ( $is_domain_only ) { + require_once __DIR__ . '/class-domain-only-admin-menu.php'; + return Domain_Only_Admin_Menu::class; + } + + // DIFM Lite In Progress Sites. Uses the same menu used for domain-only sites. + // Ignore this check if we are in a support session. + $is_difm_lite_in_progress = has_blog_sticker( 'difm-lite-in-progress' ); + $is_support_session = defined( 'WPCOM_SUPPORT_SESSION' ) && WPCOM_SUPPORT_SESSION; + if ( $is_difm_lite_in_progress && ! $is_support_session ) { + require_once __DIR__ . '/class-domain-only-admin-menu.php'; + return Domain_Only_Admin_Menu::class; + } + + // P2 sites. + require_once WP_CONTENT_DIR . '/lib/wpforteams/functions.php'; + if ( \WPForTeams\is_wpforteams_site( $blog_id ) ) { + require_once __DIR__ . '/class-p2-admin-menu.php'; + return P2_Admin_Menu::class; + } + + // Rest of simple sites. + require_once __DIR__ . '/class-wpcom-admin-menu.php'; + return WPcom_Admin_Menu::class; + } + + // Jetpack sites. + require_once __DIR__ . '/class-jetpack-admin-menu.php'; + return Jetpack_Admin_Menu::class; +} + +/** + * Filters the name of the class that customizes the admin menu. It should extends the `Base_Admin_Menu` class. + * + * @module masterbar + * + * @since 9.6.0 + * + * @param string $admin_menu_class Class name. + */ +$admin_menu_class = apply_filters( 'jetpack_admin_menu_class', get_admin_menu_class() ); +if ( should_customize_nav( $admin_menu_class ) ) { + /** The admin menu singleton instance. @var Base_Admin_Menu $instance */ + $admin_menu_class::get_instance(); + + /** + * Trigger an event when the user uses the dashboard quick switcher. + * + * @param string $screen The current screen. + * @param string $view The view the user choosed to go to. + */ + function dashboard_quick_switcher_record_usage( $screen, $view ) { + require_once __DIR__ . '/class-dashboard-switcher-tracking.php'; + + $tracking = new Dashboard_Switcher_Tracking( + new Tracking( Dashboard_Switcher_Tracking::get_jetpack_tracking_product() ), + array( Dashboard_Switcher_Tracking::class, 'wpcom_tracks_record_event' ), + Dashboard_Switcher_Tracking::get_plan() + ); + + $tracking->record_switch_event( $screen, $view ); + } + + \add_action( 'jetpack_dashboard_switcher_changed_view', __NAMESPACE__ . '\dashboard_quick_switcher_record_usage', 10, 2 ); +} else { + \add_filter( 'jetpack_load_admin_menu_class', '__return_false' ); +} diff --git a/plugins/jetpack/modules/masterbar/admin-menu/menu-mappings.php b/plugins/jetpack/modules/masterbar/admin-menu/menu-mappings.php new file mode 100644 index 00000000..f17acb9e --- /dev/null +++ b/plugins/jetpack/modules/masterbar/admin-menu/menu-mappings.php @@ -0,0 +1,31 @@ +<?php +/** + * Helper mapping between WP Admin pages and WordPress.com + * + * @package automattic/jetpack + */ + +$common_mappings = array( + 'upload.php' => 'https://wordpress.com/media/', + 'edit.php' => 'https://wordpress.com/posts/', + 'edit-comments.php' => 'https://wordpress.com/comments/', + 'import.php' => 'https://wordpress.com/import/', + 'edit.php?post_type=page' => 'https://wordpress.com/pages/', + 'edit.php?post_type=post' => 'https://wordpress.com/posts/', + 'users.php' => 'https://wordpress.com/people/team/', + 'options-general.php' => 'https://wordpress.com/settings/general/', + 'options-discussion.php' => 'https://wordpress.com/settings/discussion/', + 'options-writing.php' => 'https://wordpress.com/settings/writing/', + 'themes.php' => 'https://wordpress.com/themes/', + 'edit-tags.php?taxonomy=category' => 'https://wordpress.com/settings/taxonomies/category/', + 'edit-tags.php?taxonomy=post_tag' => 'https://wordpress.com/settings/taxonomies/post_tag/', + 'edit.php?post_type=jetpack-portfolio' => 'https://wordpress.com/types/jetpack-portfolio/', + 'edit.php?post_type=jetpack-testimonial' => 'https://wordpress.com/types/jetpack-testimonial/', +); + +if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + // WPCOM Specific mappings. + $common_mappings['export.php'] = 'https://wordpress.com/export/'; +} + +return $common_mappings; |