mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'kiryushkina/edu_fixes'
This commit is contained in:
@@ -83,9 +83,8 @@
|
||||
<action id="StudySwitchPanel" class="com.jetbrains.edu.learning.actions.StudySwitchTaskPanelAction"
|
||||
text="Switch Task Description Panel">
|
||||
</action>
|
||||
<action id="Edu.OpenTaskInStepik" class="com.jetbrains.edu.learning.actions.StudyOpenTaskInStepikAction"
|
||||
text="Open this task in Stepik">
|
||||
</action>
|
||||
|
||||
<action id="Edu.UpdateRecommendation" class="com.jetbrains.edu.learning.actions.StudyUpdateRecommendationAction"/>
|
||||
|
||||
<!--course creator-->
|
||||
<action id="CreateLesson" class="com.jetbrains.edu.coursecreator.actions.CCCreateLesson">
|
||||
|
||||
@@ -21,18 +21,37 @@
|
||||
<script src="${language_script}"></script>
|
||||
<!--suppress CssInvalidPropertyValue -->
|
||||
<style media="screen" type="text/css">
|
||||
body {
|
||||
html, body {
|
||||
height:100%;
|
||||
overflow:hidden;
|
||||
font-size: ${font_size}pt !important;
|
||||
}
|
||||
.wrapper {
|
||||
overflow-y:scroll;
|
||||
height: 96%;
|
||||
}
|
||||
|
||||
/*used to place task on Stepik link*/
|
||||
/*noinspection CssUnusedSymbol*/
|
||||
.footer {
|
||||
position:fixed;
|
||||
line-height: 3.5em;
|
||||
bottom: 0;
|
||||
height: 4%;
|
||||
width: 95%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class=wrapper>
|
||||
${code}
|
||||
</div>
|
||||
<script>
|
||||
var nodeList = document.body.getElementsByTagName("code");
|
||||
nodes = Array.prototype.slice.call(nodeList, 0);
|
||||
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
var text = node.innerHTML
|
||||
node.innerHTML = text.replace(/<br\s*>/g, '\n')
|
||||
|
||||
+2
-2
@@ -331,9 +331,9 @@ public class CCStepicConnector {
|
||||
|
||||
private static void showStepicNotification(@NotNull Project project,
|
||||
@NotNull NotificationType notificationType, @NotNull String failedActionName) {
|
||||
String text = "Authorize on Stepik to " + failedActionName;
|
||||
String text = "Log in to Stepik to " + failedActionName;
|
||||
Notification notification = new Notification("Stepik", "Failed to " + failedActionName, text, notificationType);
|
||||
notification.addAction(new AnAction("Authorize") {
|
||||
notification.addAction(new AnAction("Log in") {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
|
||||
@@ -120,7 +120,7 @@ public interface EduPluginConfigurator {
|
||||
StudyNextTaskAction.ACTION_ID,
|
||||
StudyRefreshTaskFileAction.ACTION_ID,
|
||||
StudyShowHintAction.ACTION_ID,
|
||||
StudyOpenTaskInStepikAction.ACTION_ID,
|
||||
StudyUpdateRecommendationAction.ACTION_ID
|
||||
};
|
||||
ActionManager actionManager = ActionManager.getInstance();
|
||||
Arrays.stream(ids)
|
||||
|
||||
@@ -95,6 +95,7 @@ public class StudyProjectComponent implements ProjectComponent {
|
||||
instance.setHideToolStripes(false);
|
||||
instance.fireUISettingsChanged();
|
||||
registerShortcuts();
|
||||
addStepicWidget();
|
||||
EduUsagesCollector.projectTypeOpened(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY);
|
||||
}
|
||||
})));
|
||||
@@ -110,6 +111,10 @@ public class StudyProjectComponent implements ProjectComponent {
|
||||
}
|
||||
});
|
||||
|
||||
selectStep();
|
||||
}
|
||||
|
||||
private void addStepicWidget() {
|
||||
StudyStepicUserWidget widget = StudyUtils.getStepicWidget();
|
||||
if (widget == null) {
|
||||
StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
|
||||
@@ -118,8 +123,6 @@ public class StudyProjectComponent implements ProjectComponent {
|
||||
else {
|
||||
widget.update();
|
||||
}
|
||||
|
||||
selectStep();
|
||||
}
|
||||
|
||||
private void selectStep() {
|
||||
|
||||
@@ -67,6 +67,7 @@ import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask;
|
||||
import com.jetbrains.edu.learning.editor.StudyEditor;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepikUtils;
|
||||
import com.jetbrains.edu.learning.stepic.OAuthDialog;
|
||||
import com.jetbrains.edu.learning.stepic.StepicUser;
|
||||
import com.jetbrains.edu.learning.ui.StudyStepicUserWidget;
|
||||
@@ -430,14 +431,23 @@ public class StudyUtils {
|
||||
}
|
||||
|
||||
private static String wrapAdaptiveCourseText(Task task, @NotNull String text) {
|
||||
String finalText = text;
|
||||
if (task instanceof TheoryTask) {
|
||||
return text + "\n\n<b>Note</b>: This theory task aims to help you solve difficult tasks. " +
|
||||
finalText += "\n\n<b>Note</b>: This theory task aims to help you solve difficult tasks. " +
|
||||
"Please, read it and press \"Check\" to go further.";
|
||||
}
|
||||
else if (!(task instanceof ChoiceTask)) {
|
||||
return text + "\n\n<b>Note</b>: Use standard input to obtain input for the task.";
|
||||
finalText += "\n\n<b>Note</b>: Use standard input to obtain input for the task.";
|
||||
}
|
||||
return text;
|
||||
finalText += getFooterWithLink(task);
|
||||
|
||||
return finalText;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getFooterWithLink(Task task) {
|
||||
return
|
||||
"<div class=\"footer\">" + "<a href=" + EduStepikUtils.getAdaptiveLink(task) + ">Open on Stepik</a>" + "</div>";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.edu.learning.actions;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.KeyboardShortcut;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepikUtils;
|
||||
import icons.EducationalCoreIcons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class StudyOpenTaskInStepikAction extends StudyActionWithShortcut {
|
||||
public static final String SHORTCUT = "ctrl alt pressed HOME";
|
||||
public static final String ACTION_ID = "Edu.OpenTaskInStepik";
|
||||
private static final String TEXT = "Open this task in Stepik";
|
||||
|
||||
public StudyOpenTaskInStepikAction() {
|
||||
super(getTextWithShortcuts(), TEXT, EducationalCoreIcons.Stepik);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getTextWithShortcuts() {
|
||||
return TEXT + "(" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
Task currentTask = StudyUtils.getCurrentTask(project);
|
||||
|
||||
String link = EduStepikUtils.getLink(currentTask);
|
||||
if (link != null) {
|
||||
BrowserUtil.browse(link);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
final Presentation presentation = e.getPresentation();
|
||||
StudyUtils.updateAction(e);
|
||||
Project project = e.getProject();
|
||||
if (project == null) {
|
||||
presentation.setVisible(false);
|
||||
return;
|
||||
}
|
||||
Task currentTask = StudyUtils.getCurrentTask(project);
|
||||
|
||||
String link = EduStepikUtils.getLink(currentTask);
|
||||
boolean visible = link != null;
|
||||
presentation.setVisible(visible);
|
||||
presentation.setEnabled(visible);
|
||||
if (visible) {
|
||||
presentation.setText(getTextWithShortcuts() + '\n' + link);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getActionId() {
|
||||
return ACTION_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getShortcuts() {
|
||||
return new String[]{SHORTCUT};
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package com.jetbrains.edu.learning.actions;
|
||||
|
||||
import com.intellij.ide.projectView.ProjectView;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.navigation.StudyNavigator;
|
||||
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
|
||||
import icons.EducationalCoreIcons;
|
||||
|
||||
public class StudyUpdateRecommendationAction extends DumbAwareAction {
|
||||
public static final String ACTION_ID = "Edu.UpdateRecommendation";
|
||||
|
||||
public StudyUpdateRecommendationAction() {
|
||||
super("Update course", "Update course", EducationalCoreIcons.Stepik);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
assert project != null;
|
||||
|
||||
Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
assert course != null;
|
||||
|
||||
Lesson adaptiveLesson = course.getLessons().get(0);
|
||||
assert adaptiveLesson != null;
|
||||
|
||||
int taskNumber = adaptiveLesson.getTaskList().size();
|
||||
Task lastRecommendationInCourse = adaptiveLesson.getTaskList().get(taskNumber - 1);
|
||||
Task lastRecommendationOnStepik = ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
() -> {
|
||||
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
|
||||
return StudyUtils.execCancelable(() -> EduAdaptiveStepicConnector.getNextRecommendation(project, (RemoteCourse)course));
|
||||
},
|
||||
"Updating Course", true, project);
|
||||
|
||||
if (lastRecommendationOnStepik != null && lastRecommendationOnStepik.getStepId() != lastRecommendationInCourse.getStepId()) {
|
||||
lastRecommendationOnStepik.initTask(adaptiveLesson, false);
|
||||
EduAdaptiveStepicConnector.replaceCurrentTask(project, lastRecommendationOnStepik, adaptiveLesson);
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
|
||||
ProjectView.getInstance(project).refresh();
|
||||
StudyNavigator.navigateToTask(project, lastRecommendationOnStepik);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
Presentation presentation = e.getPresentation();
|
||||
|
||||
Project project = e.getProject();
|
||||
if (project == null) {
|
||||
presentation.setEnabledAndVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
if (course == null || !course.isAdaptive()) {
|
||||
presentation.setEnabledAndVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
presentation.setEnabledAndVisible(true);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -122,14 +122,14 @@ public class EduStepikRestService extends RestService {
|
||||
if (stepicUser != null) {
|
||||
StudySettings.getInstance().setUser(stepicUser);
|
||||
sendHtmlResponse(request, context, "/oauthResponsePages/okPage.html");
|
||||
showStepicNotification(NotificationType.INFORMATION, "Authorized as " + stepicUser.getFirstName() + " " + stepicUser.getLastName());
|
||||
showStepicNotification(NotificationType.INFORMATION, "Logged in as " + stepicUser.getFirstName() + " " + stepicUser.getLastName());
|
||||
focusOnApplicationWindow();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
sendHtmlResponse(request, context, "/oauthResponsePages/errorPage.html");
|
||||
showStepicNotification(NotificationType.ERROR, "Failed to authorize");
|
||||
showStepicNotification(NotificationType.ERROR, "Failed to log in");
|
||||
return "Couldn't find code parameter for Stepik OAuth";
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.edu.learning.EduPluginConfigurator;
|
||||
import com.jetbrains.edu.learning.StudySettings;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
|
||||
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
|
||||
@@ -440,7 +441,7 @@ public class EduCoursesPanel extends JPanel {
|
||||
List<String> tags = new ArrayList<>();
|
||||
tags.add(course.getLanguageById().getDisplayName());
|
||||
if (course.isAdaptive()) {
|
||||
tags.add("Adaptive");
|
||||
tags.add(EduNames.ADAPTIVE);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
+2
-2
@@ -300,7 +300,7 @@ public class EduAdaptiveStepicConnector {
|
||||
if (user == null) {
|
||||
LOG.warn("Can't get next recommendation: user is null");
|
||||
ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project,
|
||||
"Can't get next recommendation: you're not authorized"));
|
||||
"Can't get next recommendation: you're not logged in"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceCurrentTask(@NotNull Project project, @NotNull Task task, @NotNull Lesson lesson) {
|
||||
public static void replaceCurrentTask(@NotNull Project project, @NotNull Task task, @NotNull Lesson lesson) {
|
||||
Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
assert course != null;
|
||||
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ public class EduStepicConnector {
|
||||
}
|
||||
else {
|
||||
final Lesson lesson = new Lesson();
|
||||
lesson.setName("Adaptive");
|
||||
lesson.setName(EduNames.ADAPTIVE);
|
||||
remoteCourse.addLesson(lesson);
|
||||
//TODO: more specific name?
|
||||
final Task recommendation = EduAdaptiveStepicConnector.getNextRecommendation(project, remoteCourse);
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EduStepikUtils {
|
||||
@Nullable
|
||||
public static String getLink(@Nullable Task task) {
|
||||
public static String getLink(@Nullable Task task, int stepNumber) {
|
||||
if (task == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -31,6 +31,12 @@ public class EduStepikUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
return String.format("%s/lesson/%d/step/%d", EduStepicNames.STEPIC_URL, lesson.getId(), task.getPosition());
|
||||
return String.format("%s/lesson/%d/step/%d", EduStepicNames.STEPIC_URL, lesson.getId(), stepNumber);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getAdaptiveLink(@Nullable Task task) {
|
||||
String link = getLink(task, 1);
|
||||
return link == null ? null : link + "?adaptive=true";
|
||||
}
|
||||
}
|
||||
|
||||
+11
-31
@@ -15,23 +15,19 @@
|
||||
*/
|
||||
package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DefaultProjectFactory;
|
||||
import com.intellij.ui.HoverHyperlinkLabel;
|
||||
import com.intellij.ui.HyperlinkAdapter;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.jetbrains.edu.learning.StudySettings;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.settings.StudyOptionsProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class StepicStudyOptions implements StudyOptionsProvider {
|
||||
private JPanel myPane;
|
||||
@@ -61,32 +57,16 @@ public class StepicStudyOptions implements StudyOptionsProvider {
|
||||
|
||||
@Override
|
||||
protected void hyperlinkActivated(HyperlinkEvent e) {
|
||||
StudySettings studySettings = StudySettings.getInstance();
|
||||
StepicUser oldUser = studySettings.getUser();
|
||||
EduStepicConnector.doAuthorize(() -> showDialog());
|
||||
|
||||
ProgressManager.getInstance()
|
||||
.runProcessWithProgressSynchronously(() -> {
|
||||
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
|
||||
StudyUtils.execCancelable(waitForUserToBeSet(studySettings, oldUser));
|
||||
}, "Authorizing",
|
||||
true,
|
||||
DefaultProjectFactory.getInstance().getDefaultProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Callable<Object> waitForUserToBeSet(StudySettings studySettings, StepicUser oldUser) {
|
||||
return () -> {
|
||||
StepicUser newUser = studySettings.getUser();
|
||||
while (newUser == null || newUser.equals(oldUser)) {
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
newUser = studySettings.getUser();
|
||||
ApplicationManager.getApplication().getMessageBus().connect().subscribe(StudySettings.SETTINGS_CHANGED, () -> {
|
||||
StepicUser user = StudySettings.getInstance().getUser();
|
||||
if (user != null && !user.equals(myStepicUser)) {
|
||||
StudySettings.getInstance().setUser(myStepicUser);
|
||||
myStepicUser = user;
|
||||
updateLoginLabels(myStepicUser);
|
||||
}
|
||||
StudySettings.getInstance().setUser(myStepicUser);
|
||||
myStepicUser = newUser;
|
||||
updateLoginLabels(myStepicUser);
|
||||
return null;
|
||||
};
|
||||
});
|
||||
|
||||
EduStepicConnector.doAuthorize(() -> showDialog());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -122,7 +102,7 @@ public class StepicStudyOptions implements StudyOptionsProvider {
|
||||
|
||||
if (stepicUser == null) {
|
||||
myUsernameLabel.setText("You're not logged in");
|
||||
myHoverHyperlinkLabel.setText("Authorize on Stepik");
|
||||
myHoverHyperlinkLabel.setText("Log in to Stepik");
|
||||
|
||||
myListener = createAuthorizeListener();
|
||||
myHoverHyperlinkLabel.addHyperlinkListener(myListener);
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ public class StudyStepicUserWidget implements IconLikeCustomStatusBarWidget {
|
||||
setLayout(layout);
|
||||
|
||||
if (user == null) {
|
||||
createUserPanel("You're not logged in", "Authorize on Stepik", createAuthorizeUserListener());
|
||||
createUserPanel("You're not logged in", "Log in to Stepik", createAuthorizeUserListener());
|
||||
}
|
||||
else {
|
||||
String firstName = user.getFirstName();
|
||||
|
||||
@@ -23,6 +23,5 @@ public class PyStudyInstructionPainter extends EditorEmptyTextPainter {
|
||||
appendAction(painter, "Reset current task file", getActionShortcutText(StudyRefreshTaskFileAction.ACTION_ID));
|
||||
appendAction(painter, "Check task", getActionShortcutText(StudyCheckAction.ACTION_ID));
|
||||
appendAction(painter, "Get hint for the answer placeholder", getActionShortcutText(StudyShowHintAction.ACTION_ID));
|
||||
appendAction(painter, "Open this task in Stepik", getActionShortcutText(StudyOpenTaskInStepikAction.ACTION_ID));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user