Skip to content

Commit

Permalink
Merge branch 'f24' into anonymous-posting-front-end
Browse files Browse the repository at this point in the history
  • Loading branch information
NickDevi authored Oct 20, 2024
2 parents 2fa071f + 6adf80d commit dbd38f7
Show file tree
Hide file tree
Showing 9 changed files with 1,176 additions and 316 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
[![Coverage Status](https://coveralls.io/repos/github/CMU-313/NodeBB/badge.svg)](https://coveralls.io/github/CMU-313/NodeBB)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=CMU-313_NodeBB&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=CMU-313_NodeBB)


****************************************** NOTICE ************************************************

The collaborators on this project are Seckhen Andrade, Nikoloz Devidze, Ghani Raissov, Davit Charkviani, Yousuf Alkhiyami

Please know that there is a folder called node_module_real, which contains the files that are modified compared to the files that are in the node_modules we get by npm install. Please take the code from the files in the node_modules_real folder (files have the same names as in the node_module generated by npm install) and paste (replace the code) them in the respective files.


Please download the jest dependencies through npm install jest and npm install jest-environment-jsdom. After this also add "test:jest": "jest --config ./jest.config.js", in package.json's "scripts" section. After this, run npm run test:jest, and the tests about our added material will get executed!

***************************************************************************************************


[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and supports either Redis, MongoDB, or a PostgreSQL database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB takes the best of the modern web: real-time streaming discussions, mobile responsiveness, and rich RESTful read/write APIs, while staying true to the original bulletin board/forum format → categorical hierarchies, local user accounts, and asynchronous messaging.

NodeBB by itself contains a "common core" of basic functionality, while additional functionality and integrations are enabled through the use of third-party plugins.
Expand Down
Binary file added dump.rdb
Binary file not shown.
75 changes: 75 additions & 0 deletions node_modules_real/composer-formatting.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<div class="d-flex justify-content-between gap-2 align-items-center formatting-bar m-0">
<ul class="list-unstyled mb-0 d-flex formatting-group gap-2 overflow-auto">
{{{ each formatting }}}
{{{ if ./spacer }}}
<li class="small spacer"></li>
{{{ else }}}
{{{ if (./visibility.desktop && ((isTopicOrMain && ./visibility.main) || (!isTopicOrMain && ./visibility.reply))) }}}
{{{ if ./dropdownItems.length }}}
<li class="dropdown bottom-sheet" title="{./title}">
<button class="btn btn-sm btn-link text-reset" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{./title}">
<i class="{./className}"></i>
</button>
<ul class="dropdown-menu p-1" role="menu">
{{{ each ./dropdownItems }}}
<li>
<a href="#" data-format="{./name}" class="dropdown-item rounded-1 position-relative" role="menuitem">
<i class="{./className} text-secondary"></i> {./text}
{{{ if ./badge }}}
<span class="px-1 position-absolute top-0 start-100 translate-middle badge rounded text-bg-info"></span>
{{{ end }}}
</a>
</li>
{{{ end }}}
</ul>
</li>
{{{ else }}}
<li title="{./title}">
<button data-format="{./name}" class="btn btn-sm btn-link text-reset position-relative" aria-label="{./title}">
<i class="{./className}"></i>
{{{ if ./badge }}}
<span class="px-1 position-absolute top-0 start-100 translate-middle badge rounded text-bg-info"></span>
{{{ end }}}
</button>
</li>
{{{ end }}}
{{{ end }}}
{{{ end }}}
{{{ end }}}

{{{ if privileges.upload:post:image }}}
<li title="[[modules:composer.upload-picture]]">
<button data-format="picture" class="img-upload-btn btn btn-sm btn-link text-reset" aria-label="[[modules:composer.upload-picture]]">
<i class="fa fa-file-image-o"></i>
</button>
</li>
{{{ end }}}

{{{ if privileges.upload:post:file }}}
<li title="[[modules:composer.upload-file]]">
<button data-format="upload" class="file-upload-btn btn btn-sm btn-link text-reset" aria-label="[[modules:composer.upload-file]]">
<i class="fa fa-file-o"></i>
</button>
</li>
{{{ end }}}

<form id="fileForm" method="post" enctype="multipart/form-data">
<input type="file" id="files" name="files[]" multiple class="hide"/>
</form>
</ul>
<div class="d-flex align-items-center gap-1">
<div class="draft-icon m-2 hidden-xs hidden-sm"></div>
<button class="btn btn-sm btn-link py-2 text-body fw-semibold text-nowrap" data-action="preview">
<i class="fa fa-eye"></i>
<span class="d-none d-md-inline show-text">[[modules:composer.show-preview]]</span>
<span class="d-none d-md-inline hide-text">[[modules:composer.hide-preview]]</span>
</button>
{{{ if composer:showHelpTab }}}
<button class="btn btn-sm btn-link py-2 text-body fw-semibold text-nowrap" data-action="help">
<i class="fa fa-question"></i>
<span class="d-none d-md-inline">[[modules:composer.help]]</span>
</button>
{{{ end }}}
</div>
</div>
#
171 changes: 168 additions & 3 deletions node_modules_real/post-menu-list.tpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{{{ if posts.display_moderator_tools }}}

<-- {{{ if posts.display_moderator_tools }}}

<li>
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="post/edit" role="menuitem" href="#">
<span class="menu-icon"><i class="fa fa-fw text-secondary fa-pencil"></i></span> [[topic:edit]]
Expand Down Expand Up @@ -127,13 +129,176 @@
<li>
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="post/flagResolve" data-flagId="{posts.flags.flagId}" role="menuitem" href="#"><i class="fa fa-fw text-secondary fa-check"></i> [[topic:resolve-flag]]</a>
</li>

{{{ end }}}
{{{ end }}}
{{{ end }}}


<li>
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="post/notes" role="menuitem" href="#" data-post-id="{posts.pid}">
<span class="menu-icon"><i class="fa fa-fw fa-sticky-note"></i></span> [[topic:manage-notes]]
</a>
</li>

</li>

<!-- Modal Structure -->
<div id="notesModal" style="display:none; position:fixed; top:20%; left:50%; transform:translate(-50%, -50%); width:400px; background-color:white; border:1px solid #ccc; z-index:10000; padding:20px;">
<div class="modal-header">
<h5>Manage Notes</h5>
<button id="closeModalBtn" style="background:none; border:none; font-size:20px; cursor:pointer;">&times;</button>
</div>
<div class="modal-body">
<!-- New section to display saved notes -->
<div id="savedNotesDisplay" style="border:1px solid #ccc; padding:10px; min-height:50px; background-color:#f9f9f9; margin-bottom:15px;">
<!-- Saved notes will appear here -->
</div>
<!-- Textarea for adding new notes -->
<textarea id="userNotes" rows="5" style="width: 100%;" placeholder="Write your new note here..."></textarea>
</div>
<div class="modal-footer" style="margin-top: 10px; text-align: right;">
<button id="saveNotesBtn" style="padding:10px 20px; background-color:blue; color:white; border:none; cursor:pointer;">Save</button>
<button id="closeModalFooterBtn" style="padding:10px 20px; background-color:grey; color:white; border:none; cursor:pointer;">Close</button>
</div>
</div>

<script>
// Function to safely parse JSON or return an empty array if invalid
function safelyParseJSON(value) {
try {
return JSON.parse(value);
} catch (e) {
return []; // Return an empty array if parsing fails
}
}
var currentPostId = null; // Keep track of the current post being edited
// Attach the event handler to the notes button
document.addEventListener('click', function(e) {
const notesButton = e.target.closest('a[component="post/notes"]');
if (notesButton) {
e.preventDefault();
// Try to get the post element by traversing up the DOM tree
const postElement = notesButton.closest('[data-pid], [id^="post_"], [id^="p"]');
if (postElement) {
// Try to get the postId from data-pid or id attribute
if (postElement.hasAttribute('data-pid')) {
currentPostId = postElement.getAttribute('data-pid');
} else if (postElement.id) {
// id could be in the format 'post_12345' or 'p12345'
currentPostId = postElement.id.replace('post_', '').replace('p', '');
}
}
if (!currentPostId) {
console.error('Post ID not found!');
return;
}
var storageKey = 'postNotes_' + currentPostId;
console.log('Manage Notes button clicked for post:', currentPostId);
// Get existing notes from localStorage, safely parse them as an array
var existingNotes = safelyParseJSON(localStorage.getItem(storageKey)) || [];
// Clear the display area before adding notes
var savedNotesDisplay = document.getElementById('savedNotesDisplay');
savedNotesDisplay.innerHTML = ''; // Clear the content
// Display existing notes if they exist
if (existingNotes.length > 0) {
savedNotesDisplay.innerHTML = existingNotes.map(note => note + '<br>').join('');
} else {
savedNotesDisplay.innerHTML = 'No notes yet.';
}
// Clear the new note input field
document.getElementById('userNotes').value = '';
// Show the modal
document.getElementById('notesModal').style.display = 'block';
// Close the dropdown to avoid it interfering with future clicks
closeDropdownMenu(notesButton);
// Ensure the save button doesn't get multiple listeners attached
document.getElementById('saveNotesBtn').removeEventListener('click', handleSaveButtonClick); // Remove any previous listeners
document.getElementById('saveNotesBtn').addEventListener('click', handleSaveButtonClick); // Attach the new listener
}
});
// Function to handle the save button click
function handleSaveButtonClick() {
if (!currentPostId) {
alert('Error: No post ID found.');
return;
}
var storageKey = 'postNotes_' + currentPostId;
// Get the new note from the textarea
var newNote = document.getElementById('userNotes').value.trim();
if (newNote === '') {
alert('Please write something before saving.');
return;
}
// Get existing notes from localStorage (safely parsed as an array)
var existingNotes = safelyParseJSON(localStorage.getItem(storageKey)) || [];
// Append the new note to the existing notes array
existingNotes.push(newNote);
// Save the updated notes array back to localStorage as a JSON string
localStorage.setItem(storageKey, JSON.stringify(existingNotes));
// Notify the user of success
alert('Note saved successfully.');
// Hide the modal
document.getElementById('notesModal').style.display = 'none';
// Reset the dropdown state after saving
resetDropdownState();
}
// Function to close the dropdown menu when a note button is clicked
function closeDropdownMenu(buttonElement) {
const dropdownMenu = buttonElement.closest('.dropdown-menu');
if (dropdownMenu) {
dropdownMenu.classList.remove('show');
const parentDropdown = dropdownMenu.closest('.dropdown');
if (parentDropdown) {
parentDropdown.classList.remove('show');
}
}
}
// Handle the Close button clicks (both the close icon and the footer close button)
function closeNotesModal() {
document.getElementById('notesModal').style.display = 'none';
// Reset the currentPostId and any states related to dropdowns
currentPostId = null;
// Reset the dropdown state after closing
resetDropdownState();
}
// Function to reset the dropdown state globally
function resetDropdownState() {
// Reactivate any dropdown menus that may be stuck in the "open" state
const allDropdowns = document.querySelectorAll('.dropdown-menu.show');
allDropdowns.forEach(dropdown => {
dropdown.classList.remove('show');
});
}
document.getElementById('closeModalBtn').addEventListener('click', closeNotesModal);
document.getElementById('closeModalFooterBtn').addEventListener('click', closeNotesModal);
</script> -->
56 changes: 56 additions & 0 deletions node_modules_real/topics_list.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,61 @@
chatListContainer.append(listGroup);
}
// Function to send the post content to the chat room via POST request
function sendPostToChat(roomId, postContent) {
var csrfToken = config.csrf_token; // Assuming CSRF token is available in the config
$.ajax({
url: config.relative_path + '/api/v3/chats/' + roomId, // Assuming API v3
method: 'POST',
contentType: 'application/json',
headers: {
'x-csrf-token': csrfToken // Add CSRF token to the request headers
},
data: JSON.stringify({
message: postContent
}),
success: function (data) {
console.log('Message sent successfully to Room ID:', roomId);
// Close the modal after sending
$('#shareToChatModal').modal('hide');
},
error: function (err) {
console.error('Error sending message to chat room:', err);
}
});
}
// Function to share the post to the selected chat
function sharePostToChat(roomId, tid, title) {
var postLink = 'http://localhost:4567' + '/topic/' + tid;
var postContent = 'Check out this topic: "' + title + '", ID: ' + tid + '. Here is the link: ' + postLink;
console.log('Sharing to Room ID:', roomId);
console.log('Post Content:', postContent);
// Send the post content to the chat room
sendPostToChat(roomId, postContent);
}
function fetchTopicTitle(tid, callback) {
$.ajax({
url: config.relative_path + '/api/topic/' + tid, // NodeBB API to fetch topic details
method: 'GET',
success: function (data) {
if (data && data.title) {
callback(data.title); // Return the title via the callback
} else {
callback("Unknown Topic");
}
},
error: function (err) {
console.error('Error fetching topic title:', err);
callback("Unknown Topic");
}
});
}
});
</script>
Loading

0 comments on commit dbd38f7

Please sign in to comment.