Lots of snazzy ui updates.
This commit is contained in:
@@ -471,45 +471,46 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/stegasoo.js') }}"></script>
|
||||
<script>
|
||||
// ============================================================================
|
||||
// GENERATE PAGE - Form Controls
|
||||
// ============================================================================
|
||||
|
||||
// Words range slider
|
||||
const wordsRange = document.getElementById('wordsRange');
|
||||
const wordsValue = document.getElementById('wordsValue');
|
||||
|
||||
if (wordsRange) {
|
||||
wordsRange.addEventListener('input', function() {
|
||||
const bits = this.value * 11;
|
||||
wordsValue.textContent = `${this.value} words (~${bits} bits)`;
|
||||
});
|
||||
}
|
||||
wordsRange?.addEventListener('input', function() {
|
||||
const bits = this.value * 11;
|
||||
wordsValue.textContent = `${this.value} words (~${bits} bits)`;
|
||||
});
|
||||
|
||||
// Toggle PIN/RSA options
|
||||
// Toggle PIN/RSA options visibility
|
||||
const usePinCheck = document.getElementById('usePinCheck');
|
||||
const useRsaCheck = document.getElementById('useRsaCheck');
|
||||
const pinOptions = document.getElementById('pinOptions');
|
||||
const rsaOptions = document.getElementById('rsaOptions');
|
||||
|
||||
if (usePinCheck) {
|
||||
usePinCheck.addEventListener('change', function() {
|
||||
pinOptions.classList.toggle('d-none', !this.checked);
|
||||
});
|
||||
}
|
||||
|
||||
if (useRsaCheck) {
|
||||
useRsaCheck.addEventListener('change', function() {
|
||||
rsaOptions.classList.toggle('d-none', !this.checked);
|
||||
});
|
||||
}
|
||||
|
||||
// RSA key size QR warning
|
||||
const rsaBitsSelect = document.getElementById('rsaBitsSelect');
|
||||
const rsaQrWarning = document.getElementById('rsaQrWarning');
|
||||
const rsaBitsSelect = document.getElementById('rsaBitsSelect');
|
||||
|
||||
if (rsaBitsSelect && rsaQrWarning) {
|
||||
rsaBitsSelect.addEventListener('change', function() {
|
||||
rsaQrWarning.classList.toggle('d-none', parseInt(this.value) <= 3072);
|
||||
});
|
||||
}
|
||||
usePinCheck?.addEventListener('change', function() {
|
||||
pinOptions?.classList.toggle('d-none', !this.checked);
|
||||
});
|
||||
|
||||
useRsaCheck?.addEventListener('change', function() {
|
||||
rsaOptions?.classList.toggle('d-none', !this.checked);
|
||||
});
|
||||
|
||||
// RSA key size QR warning (>3072 bits)
|
||||
rsaBitsSelect?.addEventListener('change', function() {
|
||||
rsaQrWarning?.classList.toggle('d-none', parseInt(this.value) <= 3072);
|
||||
});
|
||||
|
||||
{% if generated %}
|
||||
// ============================================================================
|
||||
// GENERATE PAGE - Credential Display
|
||||
// ============================================================================
|
||||
|
||||
// PIN visibility toggle
|
||||
let pinHidden = false;
|
||||
@@ -519,32 +520,19 @@ function togglePinVisibility() {
|
||||
const text = document.getElementById('pinToggleText');
|
||||
|
||||
pinHidden = !pinHidden;
|
||||
pinDigits?.classList.toggle('blurred', pinHidden);
|
||||
|
||||
if (pinHidden) {
|
||||
pinDigits.classList.add('blurred');
|
||||
icon.className = 'bi bi-eye';
|
||||
text.textContent = 'Show';
|
||||
} else {
|
||||
pinDigits.classList.remove('blurred');
|
||||
icon.className = 'bi bi-eye-slash';
|
||||
text.textContent = 'Hide';
|
||||
}
|
||||
if (icon) icon.className = pinHidden ? 'bi bi-eye' : 'bi bi-eye-slash';
|
||||
if (text) text.textContent = pinHidden ? 'Show' : 'Hide';
|
||||
}
|
||||
|
||||
// Copy PIN
|
||||
function copyPin() {
|
||||
const pin = '{{ pin|default("", true) }}';
|
||||
const icon = document.getElementById('pinCopyIcon');
|
||||
const text = document.getElementById('pinCopyText');
|
||||
|
||||
navigator.clipboard.writeText(pin).then(() => {
|
||||
icon.className = 'bi bi-check';
|
||||
text.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
icon.className = 'bi bi-clipboard';
|
||||
text.textContent = 'Copy';
|
||||
}, 2000);
|
||||
});
|
||||
Stegasoo.copyToClipboard(
|
||||
'{{ pin|default("", true) }}',
|
||||
document.getElementById('pinCopyIcon'),
|
||||
document.getElementById('pinCopyText')
|
||||
);
|
||||
}
|
||||
|
||||
// Passphrase visibility toggle
|
||||
@@ -555,79 +543,108 @@ function togglePassphraseVisibility() {
|
||||
const text = document.getElementById('passphraseToggleText');
|
||||
|
||||
passphraseHidden = !passphraseHidden;
|
||||
display?.classList.toggle('blurred', passphraseHidden);
|
||||
|
||||
if (passphraseHidden) {
|
||||
display.classList.add('blurred');
|
||||
icon.className = 'bi bi-eye';
|
||||
text.textContent = 'Show';
|
||||
} else {
|
||||
display.classList.remove('blurred');
|
||||
icon.className = 'bi bi-eye-slash';
|
||||
text.textContent = 'Hide';
|
||||
}
|
||||
if (icon) icon.className = passphraseHidden ? 'bi bi-eye' : 'bi bi-eye-slash';
|
||||
if (text) text.textContent = passphraseHidden ? 'Show' : 'Hide';
|
||||
}
|
||||
|
||||
// Copy passphrase
|
||||
function copyPassphrase() {
|
||||
const passphrase = '{{ passphrase|default("", true) }}';
|
||||
const icon = document.getElementById('passphraseCopyIcon');
|
||||
const text = document.getElementById('passphraseCopyText');
|
||||
|
||||
navigator.clipboard.writeText(passphrase).then(() => {
|
||||
icon.className = 'bi bi-check';
|
||||
text.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
icon.className = 'bi bi-clipboard';
|
||||
text.textContent = 'Copy';
|
||||
}, 2000);
|
||||
});
|
||||
Stegasoo.copyToClipboard(
|
||||
'{{ passphrase|default("", true) }}',
|
||||
document.getElementById('passphraseCopyIcon'),
|
||||
document.getElementById('passphraseCopyText')
|
||||
);
|
||||
}
|
||||
|
||||
// Memory Aid Story Generation
|
||||
// ============================================================================
|
||||
// Memory Aid Story Generation - Templates by word count
|
||||
// ============================================================================
|
||||
|
||||
const passphrase = '{{ passphrase|default("", true) }}';
|
||||
const passphraseWords = passphrase.split(' ').filter(w => w.length > 0);
|
||||
let currentStoryTemplate = 0;
|
||||
|
||||
// Story templates - words are inserted in order
|
||||
const storyTemplates = [
|
||||
// Adventure template
|
||||
words => `Once upon a time, a brave explorer named ${highlight(words[0])} set out on a quest. Along the way, they discovered a mysterious ${highlight(words[1])} hidden in an ancient ${highlight(words[2])}${words[3] ? `. With the help of a magical ${highlight(words[3])}` : ''}${words[4] ? `, they unlocked the secrets of the ${highlight(words[4])}` : ''}${words[5] ? ` and found the legendary ${highlight(words[5])}` : ''}. And they all lived happily ever after.`,
|
||||
|
||||
// Detective template
|
||||
words => `Detective ${highlight(words[0])} was investigating a curious case involving a stolen ${highlight(words[1])}. The only clue was a ${highlight(words[2])} found at the scene${words[3] ? `. Suddenly, a witness mentioned seeing a suspicious ${highlight(words[3])}` : ''}${words[4] ? ` near the old ${highlight(words[4])}` : ''}${words[5] ? `. The case was solved when they discovered the ${highlight(words[5])} contained the answer` : ''}.`,
|
||||
|
||||
// Science fiction template
|
||||
words => `In the year 2150, Captain ${highlight(words[0])} commanded the starship ${highlight(words[1])}. Their mission: to explore the ${highlight(words[2])} sector of space${words[3] ? ` where ancient ${highlight(words[3])} technology was rumored to exist` : ''}${words[4] ? `. The crew discovered a portal to the ${highlight(words[4])} dimension` : ''}${words[5] ? `, containing the mythical ${highlight(words[5])}` : ''}.`,
|
||||
|
||||
// Restaurant template
|
||||
words => `Chef ${highlight(words[0])} opened a new restaurant called "The ${highlight(words[1])}." The signature dish featured ${highlight(words[2])} as the main ingredient${words[3] ? `, garnished with fresh ${highlight(words[3])}` : ''}${words[4] ? `. Customers loved the special ${highlight(words[4])} sauce` : ''}${words[5] ? ` served in a ${highlight(words[5])}-shaped bowl` : ''}.`,
|
||||
|
||||
// Journey template
|
||||
words => `The journey began at ${highlight(words[0])} Mountain, where travelers would gather their ${highlight(words[1])} supplies. The path led through the ${highlight(words[2])} valley${words[3] ? `, past the ancient ${highlight(words[3])} ruins` : ''}${words[4] ? `, until reaching the sacred ${highlight(words[4])} temple` : ''}${words[5] ? ` where the golden ${highlight(words[5])} awaited` : ''}.`,
|
||||
|
||||
// Music template
|
||||
words => `${highlight(words[0])} was a talented musician who played the ${highlight(words[1])} in the city square. Their music sounded like ${highlight(words[2])}${words[3] ? ` mixed with ${highlight(words[3])}` : ''}${words[4] ? `. People said it reminded them of ${highlight(words[4])}` : ''}${words[5] ? ` during a beautiful ${highlight(words[5])} sunset` : ''}.`,
|
||||
|
||||
// Inventor template
|
||||
words => `Professor ${highlight(words[0])} invented a revolutionary device powered by ${highlight(words[1])} energy. It could transform ordinary ${highlight(words[2])}${words[3] ? ` into magnificent ${highlight(words[3])}` : ''}${words[4] ? `. The invention won the prestigious ${highlight(words[4])} Prize` : ''}${words[5] ? ` and was displayed at the ${highlight(words[5])} Museum` : ''}.`,
|
||||
|
||||
// Garden template
|
||||
words => `In the enchanted garden, ${highlight(words[0])} flowers bloomed beside a ${highlight(words[1])} fountain. The garden keeper planted ${highlight(words[2])} seeds${words[3] ? ` near the ${highlight(words[3])} hedge` : ''}${words[4] ? `, creating a path to the ${highlight(words[4])} arbor` : ''}${words[5] ? ` where butterflies gathered on ${highlight(words[5])} petals` : ''}.`
|
||||
];
|
||||
// Templates organized by word count (3-12 words supported)
|
||||
const storyTemplatesByLength = {
|
||||
3: [
|
||||
w => `The ${hl(w[0])} ${hl(w[1])} ${hl(w[2])}.`,
|
||||
w => `${hl(w[0])} loves ${hl(w[1])} and ${hl(w[2])}.`,
|
||||
w => `A ${hl(w[0])} found a ${hl(w[1])} near the ${hl(w[2])}.`,
|
||||
w => `${hl(w[0])}, ${hl(w[1])}, ${hl(w[2])} — never forget.`,
|
||||
w => `The ${hl(w[0])} hid the ${hl(w[1])} under the ${hl(w[2])}.`,
|
||||
],
|
||||
4: [
|
||||
w => `${hl(w[0])} and ${hl(w[1])} discovered a ${hl(w[2])} made of ${hl(w[3])}.`,
|
||||
w => `The ${hl(w[0])} ${hl(w[1])} ate ${hl(w[2])} for ${hl(w[3])}.`,
|
||||
w => `In the ${hl(w[0])}, a ${hl(w[1])} met a ${hl(w[2])} carrying ${hl(w[3])}.`,
|
||||
w => `${hl(w[0])} said "${hl(w[1])}" while holding a ${hl(w[2])} ${hl(w[3])}.`,
|
||||
w => `The secret: ${hl(w[0])}, ${hl(w[1])}, ${hl(w[2])}, ${hl(w[3])}.`,
|
||||
],
|
||||
5: [
|
||||
w => `${hl(w[0])} traveled to ${hl(w[1])} seeking the ${hl(w[2])} of ${hl(w[3])} and ${hl(w[4])}.`,
|
||||
w => `The ${hl(w[0])} ${hl(w[1])} lived in a ${hl(w[2])} house with ${hl(w[3])} ${hl(w[4])}.`,
|
||||
w => `"${hl(w[0])}!" shouted ${hl(w[1])} as the ${hl(w[2])} ${hl(w[3])} flew toward ${hl(w[4])}.`,
|
||||
w => `Captain ${hl(w[0])} sailed the ${hl(w[1])} ${hl(w[2])} searching for ${hl(w[3])} ${hl(w[4])}.`,
|
||||
w => `In ${hl(w[0])} kingdom, ${hl(w[1])} guards protected the ${hl(w[2])} ${hl(w[3])} ${hl(w[4])}.`,
|
||||
],
|
||||
6: [
|
||||
w => `${hl(w[0])} met ${hl(w[1])} at the ${hl(w[2])}. Together they found ${hl(w[3])}, ${hl(w[4])}, and ${hl(w[5])}.`,
|
||||
w => `The ${hl(w[0])} ${hl(w[1])} wore a ${hl(w[2])} hat while eating ${hl(w[3])} ${hl(w[4])} ${hl(w[5])}.`,
|
||||
w => `Detective ${hl(w[0])} found ${hl(w[1])} ${hl(w[2])} near the ${hl(w[3])} ${hl(w[4])} ${hl(w[5])}.`,
|
||||
w => `In the ${hl(w[0])} ${hl(w[1])}, a ${hl(w[2])} ${hl(w[3])} sang about ${hl(w[4])} ${hl(w[5])}.`,
|
||||
w => `Chef ${hl(w[0])} combined ${hl(w[1])}, ${hl(w[2])}, ${hl(w[3])}, ${hl(w[4])}, and ${hl(w[5])}.`,
|
||||
],
|
||||
7: [
|
||||
w => `${hl(w[0])} and ${hl(w[1])} walked through the ${hl(w[2])} ${hl(w[3])} to find the ${hl(w[4])} ${hl(w[5])} ${hl(w[6])}.`,
|
||||
w => `The ${hl(w[0])} professor studied ${hl(w[1])} ${hl(w[2])} while drinking ${hl(w[3])} ${hl(w[4])} with ${hl(w[5])} ${hl(w[6])}.`,
|
||||
w => `"${hl(w[0])} ${hl(w[1])}!" yelled ${hl(w[2])} as ${hl(w[3])} ${hl(w[4])} attacked the ${hl(w[5])} ${hl(w[6])}.`,
|
||||
w => `In ${hl(w[0])}, King ${hl(w[1])} decreed that ${hl(w[2])} ${hl(w[3])} must honor ${hl(w[4])} ${hl(w[5])} ${hl(w[6])}.`,
|
||||
],
|
||||
8: [
|
||||
w => `${hl(w[0])} ${hl(w[1])} and ${hl(w[2])} ${hl(w[3])} met at the ${hl(w[4])} ${hl(w[5])} to discuss ${hl(w[6])} ${hl(w[7])}.`,
|
||||
w => `The ${hl(w[0])} ${hl(w[1])} ${hl(w[2])} traveled from ${hl(w[3])} to ${hl(w[4])} carrying ${hl(w[5])} ${hl(w[6])} ${hl(w[7])}.`,
|
||||
w => `${hl(w[0])} discovered that ${hl(w[1])} ${hl(w[2])} plus ${hl(w[3])} ${hl(w[4])} equals ${hl(w[5])} ${hl(w[6])} ${hl(w[7])}.`,
|
||||
],
|
||||
9: [
|
||||
w => `${hl(w[0])} ${hl(w[1])} ${hl(w[2])} watched as ${hl(w[3])} ${hl(w[4])} ${hl(w[5])} danced with ${hl(w[6])} ${hl(w[7])} ${hl(w[8])}.`,
|
||||
w => `In the ${hl(w[0])} ${hl(w[1])} ${hl(w[2])}, three friends — ${hl(w[3])}, ${hl(w[4])}, ${hl(w[5])} — found ${hl(w[6])} ${hl(w[7])} ${hl(w[8])}.`,
|
||||
w => `The recipe: ${hl(w[0])}, ${hl(w[1])}, ${hl(w[2])}, ${hl(w[3])}, ${hl(w[4])}, ${hl(w[5])}, ${hl(w[6])}, ${hl(w[7])}, ${hl(w[8])}.`,
|
||||
],
|
||||
10: [
|
||||
w => `${hl(w[0])} ${hl(w[1])} told ${hl(w[2])} ${hl(w[3])} about the ${hl(w[4])} ${hl(w[5])} ${hl(w[6])} hidden in ${hl(w[7])} ${hl(w[8])} ${hl(w[9])}.`,
|
||||
w => `The ${hl(w[0])} ${hl(w[1])} ${hl(w[2])} ${hl(w[3])} ${hl(w[4])} lived beside ${hl(w[5])} ${hl(w[6])} ${hl(w[7])} ${hl(w[8])} ${hl(w[9])}.`,
|
||||
],
|
||||
11: [
|
||||
w => `${hl(w[0])} ${hl(w[1])} ${hl(w[2])} and ${hl(w[3])} ${hl(w[4])} ${hl(w[5])} discovered ${hl(w[6])} ${hl(w[7])} ${hl(w[8])} ${hl(w[9])} ${hl(w[10])}.`,
|
||||
w => `In ${hl(w[0])} ${hl(w[1])}, the ${hl(w[2])} ${hl(w[3])} ${hl(w[4])} sang of ${hl(w[5])} ${hl(w[6])} ${hl(w[7])} ${hl(w[8])} ${hl(w[9])} ${hl(w[10])}.`,
|
||||
],
|
||||
12: [
|
||||
w => `${hl(w[0])} ${hl(w[1])} ${hl(w[2])} met ${hl(w[3])} ${hl(w[4])} ${hl(w[5])} at the ${hl(w[6])} ${hl(w[7])} ${hl(w[8])} ${hl(w[9])} ${hl(w[10])} ${hl(w[11])}.`,
|
||||
w => `The twelve treasures: ${hl(w[0])}, ${hl(w[1])}, ${hl(w[2])}, ${hl(w[3])}, ${hl(w[4])}, ${hl(w[5])}, ${hl(w[6])}, ${hl(w[7])}, ${hl(w[8])}, ${hl(w[9])}, ${hl(w[10])}, ${hl(w[11])}.`,
|
||||
],
|
||||
};
|
||||
|
||||
function highlight(word) {
|
||||
function hl(word) {
|
||||
return `<span class="passphrase-word">${word}</span>`;
|
||||
}
|
||||
|
||||
function generateStory(templateIndex = null) {
|
||||
if (passphraseWords.length === 0) return '';
|
||||
function generateStory(idx = null) {
|
||||
const count = passphraseWords.length;
|
||||
if (count === 0) return '';
|
||||
|
||||
if (templateIndex === null) {
|
||||
templateIndex = currentStoryTemplate;
|
||||
// Clamp to supported range (3-12)
|
||||
const templateKey = Math.max(3, Math.min(12, count));
|
||||
const templates = storyTemplatesByLength[templateKey];
|
||||
|
||||
if (!templates || templates.length === 0) {
|
||||
// Fallback: just list the words
|
||||
return passphraseWords.map(w => hl(w)).join(' — ');
|
||||
}
|
||||
|
||||
const template = storyTemplates[templateIndex % storyTemplates.length];
|
||||
return template(passphraseWords);
|
||||
const templateIdx = (idx ?? currentStoryTemplate) % templates.length;
|
||||
return templates[templateIdx](passphraseWords);
|
||||
}
|
||||
|
||||
function toggleMemoryAid() {
|
||||
@@ -635,24 +652,22 @@ function toggleMemoryAid() {
|
||||
const icon = document.getElementById('memoryAidIcon');
|
||||
const text = document.getElementById('memoryAidText');
|
||||
|
||||
if (container.classList.contains('d-none')) {
|
||||
// Show memory aid
|
||||
container.classList.remove('d-none');
|
||||
icon.className = 'bi bi-lightbulb-fill';
|
||||
text.textContent = 'Hide Aid';
|
||||
|
||||
// Generate initial story
|
||||
const isHidden = container?.classList.contains('d-none');
|
||||
container?.classList.toggle('d-none', !isHidden);
|
||||
|
||||
if (icon) icon.className = isHidden ? 'bi bi-lightbulb-fill' : 'bi bi-lightbulb';
|
||||
if (text) text.textContent = isHidden ? 'Hide Aid' : 'Memory Aid';
|
||||
|
||||
if (isHidden) {
|
||||
document.getElementById('memoryStory').innerHTML = generateStory();
|
||||
} else {
|
||||
// Hide memory aid
|
||||
container.classList.add('d-none');
|
||||
icon.className = 'bi bi-lightbulb';
|
||||
text.textContent = 'Memory Aid';
|
||||
}
|
||||
}
|
||||
|
||||
function regenerateStory() {
|
||||
currentStoryTemplate = (currentStoryTemplate + 1) % storyTemplates.length;
|
||||
const count = passphraseWords.length;
|
||||
const templateKey = Math.max(3, Math.min(12, count));
|
||||
const templates = storyTemplatesByLength[templateKey] || [];
|
||||
currentStoryTemplate = (currentStoryTemplate + 1) % Math.max(1, templates.length);
|
||||
document.getElementById('memoryStory').innerHTML = generateStory(currentStoryTemplate);
|
||||
}
|
||||
|
||||
@@ -662,46 +677,29 @@ function printQrCode() {
|
||||
if (!qrImg) return;
|
||||
|
||||
const printWindow = window.open('', '_blank');
|
||||
printWindow.document.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Stegasoo RSA Key QR Code</title>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
img { max-width: 400px; }
|
||||
.warning {
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
border: 2px solid #ff9800;
|
||||
background: #fff3e0;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Stegasoo RSA Private Key</h2>
|
||||
<img src="${qrImg.src}" alt="RSA Key QR Code">
|
||||
<div class="warning">
|
||||
<strong>⚠️ SECURITY WARNING</strong><br>
|
||||
This QR code contains your unencrypted RSA private key.<br>
|
||||
Store securely and destroy after use.
|
||||
</div>
|
||||
<script>window.onload = function() { window.print(); }<\/script>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
printWindow.document.write(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Stegasoo RSA Key QR Code</title>
|
||||
<style>
|
||||
body { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; margin: 0; font-family: sans-serif; }
|
||||
img { max-width: 400px; }
|
||||
.warning { margin-top: 20px; padding: 10px; border: 2px solid #ff9800; background: #fff3e0; max-width: 400px; text-align: center; font-size: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Stegasoo RSA Private Key</h2>
|
||||
<img src="${qrImg.src}" alt="RSA Key QR Code">
|
||||
<div class="warning">
|
||||
<strong>⚠️ SECURITY WARNING</strong><br>
|
||||
This QR code contains your unencrypted RSA private key.<br>
|
||||
Store securely and destroy after use.
|
||||
</div>
|
||||
<script>window.onload = function() { window.print(); }<\/script>
|
||||
</body>
|
||||
</html>`);
|
||||
printWindow.document.close();
|
||||
}
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user