Просмотр исходного кода

Added a functionality to send messages and forward them via mail

Iñigo Valentin 4 лет назад
Родитель
Сommit
35aee278a4

+ 4 - 0
.gitignore

@@ -36,3 +36,7 @@ yarn-debug.log*
 .yarn-integrity
 .project
 .custom-data
+
+/config/credentials/development.key
+
+/config/credentials/production.key

+ 3 - 0
Gemfile

@@ -28,6 +28,9 @@ gem 'jbuilder', '~> 2.7'
 # Reduces boot times through caching; required in config/boot.rb
 gem 'bootsnap', '>= 1.4.4', require: false
 
+gem 'turbolinks_render'
+
+
 group :development, :test do
   # Call 'byebug' anywhere in the code to stop execution and get a debugger console
   gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]

+ 6 - 3
Gemfile.lock

@@ -112,8 +112,6 @@ GEM
       nio4r (~> 2.0)
     racc (1.6.0)
     rack (2.2.3)
-    rack-mini-profiler (2.3.3)
-      rack (>= 1.2.0)
     rack-proxy (0.7.0)
       rack
     rack-test (1.1.0)
@@ -179,6 +177,11 @@ GEM
     turbolinks (5.2.1)
       turbolinks-source (~> 5.2)
     turbolinks-source (5.2.0)
+    turbolinks_render (0.9.21)
+      actionpack (>= 5.2.0)
+      activesupport (>= 5.2.0)
+      railties (>= 5.2.0)
+      turbolinks-source (~> 5.1)
     tzinfo (2.0.4)
       concurrent-ruby (~> 1.0)
     web-console (4.2.0)
@@ -213,12 +216,12 @@ DEPENDENCIES
   listen (~> 3.3)
   mysql2 (~> 0.5)
   puma (~> 5.0)
-  rack-mini-profiler (~> 2.0)
   rails (~> 6.1.4, >= 6.1.4.1)
   sass-rails (>= 6)
   selenium-webdriver
   spring
   turbolinks (~> 5)
+  turbolinks_render
   tzinfo-data
   web-console (>= 4.1.0)
   webdrivers

+ 103 - 0
app/assets/javascripts/main.js

@@ -0,0 +1,103 @@
+function showContactForm(){
+    clearContactErrors();
+    document.getElementById('contact_menu').style.display = "block";
+    document.getElementById('contact_progress').style.display = "none";
+    document.getElementById('contact_confirmation').style.display = "none";
+    document.getElementById('contact_bg').style.opacity = "0.6";
+    document.getElementById('contact_bg').style.display = "block";
+    document.getElementById('contact').style.display = "block";
+}
+function closeContactForm(){
+    document.getElementById('contact_bg').style.opacity = "0.0";
+    document.getElementById('contact_bg').style.display = "none";
+    document.getElementById('contact').style.display = "none";
+}
+function clearContactErrors(){
+    document.getElementById('sender_name_error').style.opacity = "0";
+    document.getElementById('sender_email_error').style.opacity = "0";
+    document.getElementById('text_error').style.opacity = "0";
+}
+function contactSuccess(){
+    clearContactErrors();
+    document.getElementById('contact_sender_name').value = "";
+    document.getElementById('contact_sender_email').value = "";
+    document.getElementById('contact_text').value = "";
+    document.getElementById('contact_menu').style.display = "none";
+    document.getElementById('contact_progress').style.display = "none";
+    document.getElementById('contact_confirmation').style.display = "block";
+}
+function contactError(response){
+    document.getElementById('contact_menu').style.display = "block";
+    document.getElementById('contact_progress').style.display = "none";
+    document.getElementById('contact_confirmation').style.display = "none";
+    var errors = JSON.parse(response.replace(/\s/g, '')).response.errors
+    for (let i = 0; i < errors.length; i++) {
+        if (errors[i] === "INVALID_EMAIL"){
+            document.getElementById('sender_email_error').style.display = "inline";
+        }
+        if (errors[i] === "INVALID_TEXT"){
+            document.getElementById('text_error').style.display = "inline";
+        }
+    }
+    
+}
+function sendMessage(e) {
+    // Prevent form submit
+    if (e.preventDefault) e.preventDefault();
+
+    // Hide previously shown errors
+    clearContactErrors();
+
+    // Validate fields
+    var success = true;
+    var sender_name = document.getElementById('contact_sender_name').value;
+    var sender_email = document.getElementById('contact_sender_email').value;
+    var text = document.getElementById('contact_text').value;
+    
+    // Sender name is always OK
+    // Sender email has to look like an email
+    const res = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+    if (res.test(String(sender_email).toLowerCase()) === false){
+        success = false;
+        document.getElementById('sender_email_error').style.opacity = "1";
+    }
+    // Text needs to have something
+    if (text.length < 1){
+        success = false;
+        document.getElementById('text_error').style.opacity = "1";
+    }
+    if (success === false){
+        return false;
+    }
+    
+    // Send request
+    var url = "/contact/";
+    var xhr = new XMLHttpRequest();
+    xhr.open("POST", url, true);
+    //xhr.setRequestHeader("Content-Type", "application/json");
+    xhr.onreadystatechange = function () {
+        if (xhr.readyState === 4) {
+            if (xhr.status === 200){
+                contactSuccess();
+            }
+            else{
+                contactError(xhr.responseText)
+            }
+        }};
+    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+    datastring =
+      'authenticity_token=' + encodeURI(document.getElementById('authenticity_token').value) +
+      '&sender_name=' + encodeURI(sender_name) +
+      '&sender_email=' + encodeURI(sender_email) +
+      '&text=' + encodeURI(text)
+    document.getElementById('contact_menu').style.display = "none";
+    document.getElementById('contact_progress').style.display = "block";
+    document.getElementById('contact_confirmation').style.display = "none";
+    xhr.send(datastring);
+
+
+    // You must return false to prevent the default form behavior
+    return false;
+}
+

+ 123 - 9
app/assets/stylesheets/ui.css

@@ -294,6 +294,121 @@ footer table#footer_table td#footer_right a.lang img:hover{
         box-shadow: 0 0 0 #dde;
     }
 }
+/* The contact form */
+div#contact_bg{
+    display: none;
+    background: #000000;
+    opacity: 0.6;
+    transition: all .4s ease-in-out;
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+}
+section#contact{
+    display: none;
+    position: fixed;
+    top: 20%;
+    width: 60%;
+    margin: auto;
+    left: 20%;
+}
+@media screen and (max-width : 800px){
+    section#contact{
+        width: 90%;
+        top: 15%;
+        left: 5%;
+    }
+}
+section#contact h3 img#contact_close{
+    float:right;
+    width: 1em;
+    height: 1em;
+}
+section#contact div.contact_buttons{
+    text-align: center;
+}
+section#contact div.contact_buttons input{
+    margin: 1em;
+}
+@media screen and (max-width : 800px){
+    section#contact div.contact_buttons input{
+        margin: 0.3em;
+    }
+}
+section#contact div#contact_menu{
+    height: 18em;
+}
+section#contact div#contact_menu table{
+    width: 100%;
+}
+section#contact div#contact_menu table td.label{
+    width: 8em;
+    text-align: right;
+    padding-right: 1em;
+    vertical-align: top;
+}
+section#contact div#contact_menu table input[type=text]{
+    width: 18em;
+}
+@media screen and (max-width : 800px){
+    section#contact div#contact_menu table td.label{
+        width: initial;
+        padding-right: 0.5em;
+        padding-bottom: 1em;
+    }
+    section#contact div#contact_menu table input[type=text]{
+        width: 90%;
+    }
+}
+section#contact div#contact_menu table textarea{
+    width: 90%;
+    height: 5em;
+    margin: auto;
+}
+section#contact div#contact_menu span.contact_error {
+    font-weight: bold;
+    color: #581010;
+    background-color: #E2AAAA;
+    border: 0.2em solid #E02323;
+    border-radius: 0.429em;
+    padding: 0.2em 0.5em;
+    text-shadow: 0 0 0.15em #0003;
+    cursor: pointer;
+    font-size: 80%;
+    margin-top: -0.5em;
+    margin-left: 1em;
+    transition: all .2s ease-in-out;
+}
+section#contact div#contact_progress{
+    height: 18em;
+    text-align: center;
+}
+section#contact div#contact_progress img{
+    margin-top: 6em;
+    width: 3em;
+    height: 3em;
+    animation: contact_progress 4s infinite linear;
+}
+@keyframes contact_progress {
+    from {
+        transform: rotate(0deg);
+    }
+    to {
+        transform: rotate(359deg);
+    }
+}
+section#contact div#contact_confirmation{
+    height: 18em;
+    text-align: center;
+}
+section#contact div#contact_confirmation p{
+    padding-top: 2em;
+    padding-bottom: 2em;
+    font-size: 150%;
+    font-weight: bold;
+}
 main{
     background-color: #000000;
     width: 100%;
@@ -464,7 +579,7 @@ input[type=submit]{
 }
 
 /* Style forr buttons */
-input[type=button]{
+input[type=button], input[type=submit]{
     cursor: pointer;
     display: inline-block;
     color: #cccccc;
@@ -483,9 +598,9 @@ input[type=button]{
     text-transform: capitalize;
 }
 @media screen and (max-width : 800px){
-    input[type=button]{
-        font-size: 120%;    
-        padding: 0.9em 1.4em;
+    input[type=button], input[type=submit]{
+        /*font-size: 120%;    
+        padding: 0.9em 1.4em;*/
     }
 }
 input[type=button]::first-letter{
@@ -523,12 +638,11 @@ input[type=checkbox]{
 /* Style for textareas */
 textarea{
     width: 80%;
-    font-weight: bold;
-    color: #444444;
-    background-color: #bbbbbb;
-    border: 0.286em solid #dddddd; 
+    color: #224422;
+    background-color: #bbccbb;
+    border: 0.286em solid #ddeedd; 
     border-color: #dddddd;
-    border-radius: 0.286em;
+    border-radius: 0.429em;
     padding: 0.25em;
     text-shadow: 0 0 0.5em #ffffff;
     resize: none;

+ 2 - 3
app/controllers/application_controller.rb

@@ -2,11 +2,10 @@ class ApplicationController < ActionController::Base
 
     before_action :set_locale
     before_action :set_user
-
-    
+    skip_before_action :verify_authenticity_token
 
     private
-    
+
     def set_user
         @user = User.find(1)
     end

+ 53 - 0
app/controllers/contact_controller.rb

@@ -0,0 +1,53 @@
+class ContactController < ApplicationController
+    def index
+        @message = Message.new
+    end
+    def create
+        # Validate message fields
+        valid = true
+        @errors = []
+        # Name is irrelevant, don't vlaidate
+        # Email has to LOOK like an email
+        if !(request.POST["sender_email"].match(URI::MailTo::EMAIL_REGEXP))
+             valid = false
+             @errors.push("INVALID_EMAIL")
+        end
+        # Text needs to have something
+        if request.POST["text"].length < 1
+            valid = false
+            @error.push("INVALID_TEXT")
+        end
+
+        # If valid, save and send the email
+        if valid
+            @message = Message.new()
+            @message.user_id = @user.id
+            @message.sender_name = request.POST["sender_name"]
+            @message.sender_email = request.POST["sender_email"]
+            @message.text = request.POST["text"]
+            @message.dtime = Time.now
+            @message.language = I18n.locale
+            @message.ip = request.remote_ip
+            
+        
+            if @message.save
+                ApplicationMailer.message_email(@user, @message).deliver_later
+            else
+                print("Error saving message: \n")
+                print("   FROM: " + request.POST["sender_name"] + "(" + request.POST["sender_email"] + ")\n")
+                print("   TEXT: " + request.POST["text"] + ")\n")
+                print(@message.errors.full_messages)
+                print("\n")
+            end
+            respond_to do |format|
+                format.json { render 'contact/success'}
+            end
+        # If invalid, return the error
+        else
+            respond_to do |format|
+                format.json { render 'contact/error', status: 400}
+            end
+        end
+    end
+
+end

+ 2 - 2
app/controllers/help_controller.rb

@@ -1,4 +1,4 @@
 class HelpController < ApplicationController
-  def index
-  end
+    def index
+    end
 end

+ 13 - 2
app/mailers/application_mailer.rb

@@ -1,4 +1,15 @@
 class ApplicationMailer < ActionMailer::Base
-  default from: 'from@example.com'
-  layout 'mailer'
+    layout 'mailer'
+    default from: Rails.application.credentials.smtp[:username]
+   
+    def message_email(user, message)
+        @message = message
+        to = user.mail_adresses[0].address
+        print("SENDING EMAIL\n")
+        print("SENDING TO " + to + "\n")
+        print("DOMAIN TO " + Rails.application.credentials.smtp[:domain] + "\n")
+        print("AUTH " + Rails.application.credentials.smtp[:authentication] + "\n")
+        to = Rails.application.credentials.smtp[:username]
+        mail(to: to, subject: 'New message')
+    end
 end

+ 5 - 0
app/models/message.rb

@@ -0,0 +1,5 @@
+class Message < ApplicationRecord
+    belongs_to :user
+    validates :sender_email, presence: true
+    validates :text, presence: true, length: { minimum: 10 }
+end

+ 26 - 0
app/views/application_mailer/message_email.html.erb

@@ -0,0 +1,26 @@
+<div>
+    New message recieved on the website:
+    <ul>
+        <li>
+            <span>
+                From:
+            </span>
+            <%= @message.sender_name %>
+        </li>
+        <li>
+            <span>
+                eMail:
+            </span>
+            <%= @message.sender_email %>
+        </li>
+        <li>
+            <span>
+                Time:
+            </span>
+            <%= @message.dtime %>
+        </li>
+    </ul>
+</div>
+<pre>
+    <%= @message.text %>
+</pre>

+ 18 - 0
app/views/contact/error.erb

@@ -0,0 +1,18 @@
+{
+    "response": {
+        "status": "ERROR",
+        "errors": [
+            <%
+                i = 0
+                max = @errors.length
+                @errors.each do |error|
+            %>
+                    "<%= error %>"
+                    <% if (i + 1) < max %>
+                        ,
+                    <% end %>
+                    <% i = i + 1 %>
+            <% end %>
+        ]
+    }
+}

+ 5 - 0
app/views/contact/success.erb

@@ -0,0 +1,5 @@
+{
+  "response": {
+    "status": "SUCCESS"
+  }
+}

+ 90 - 0
app/views/layouts/_contact_form.html.erb

@@ -0,0 +1,90 @@
+<div id='contact_bg'>
+</div>
+<section id='contact' >
+    <h3>
+        <%= t("contact.title") %>
+        <span id='contact_close'>
+            <%=
+                link_to(
+                    image_tag(
+                        "control/close.svg", 
+                        id: "contact_close",
+                        alt: t("contact.cancel")
+                    ),
+                    #"mailto:" + @user.mail_adresses[0].address,
+                    "javascript:closeContactForm()"
+                )
+            %>
+        </span>
+    </h3>
+    <div id='contact_menu'>
+        <form id='contact_form' action='/<%=I18n.locale%>/contact/' method='post'>
+            <%= hidden_field_tag :authenticity_token, form_authenticity_token %>
+            <table>
+                <tr>
+                    <td class='label'>
+                        <%= t("contact.name") %>
+                    </td>
+                    <td class='input'>
+                        <input type='text' name='sender_name' id='contact_sender_name'/>
+                        <br/>
+                        <span class='contact_error' id='sender_name_error'>
+                        </span>
+                    </td>
+                </tr>
+                <tr>
+                    <td class='label'>
+                        <%= t("contact.mail") %>
+                    </td>
+                    <td class='input'>
+                        <input type='text' name='sender_email' id='contact_sender_email'/>
+                        <br/>
+                        <span class='contact_error' id='sender_email_error'>
+                            <%= t('contact.error.invalid_email') %>
+                        </span>
+                    </td>
+                </tr>
+                <tr>
+                    <td class='label'>
+                        <%= t("contact.message") %>
+                    </td>
+                    <td class='input'>
+                        <textarea type='text' name='text' id='contact_text'></textarea>
+                        <br/>
+                        <span class='contact_error' id='text_error'>
+                            <%= t('contact.error.empty_text') %>
+                        </span>
+                    </td>
+                </tr>
+            </table>
+            <div class='contact_buttons'>
+                <input type='button' value='<%=t("contact.cancel")%>' onClick='closeContactForm();'>
+                <input type='submit' value='<%=t("contact.send")%>'>
+            </div>
+        </form>
+    </div>
+    <div id='contact_progress'>
+        <%=
+            image_tag(
+                "social/email.svg", 
+                alt: ""
+            )
+        %>
+    </div>
+    <div id='contact_confirmation'>
+        <p>
+            <%= t('contact.confirmation') %>
+        </p>
+        <div class='contact_buttons'>
+            <input type='button' value='<%=t("contact.close")%>' onClick='closeContactForm();'>
+        </div>
+    </div>
+    <script>
+        var form = document.getElementById('contact_form');
+        if (form.attachEvent) {
+            form.attachEvent("submit", sendMessage);
+        } else {
+            form.addEventListener("submit", sendMessage);
+        }
+    </script>
+</section>

+ 19 - 7
app/views/layouts/_footer.html.erb

@@ -6,19 +6,31 @@
                 <span id='footer_follow' class='desktop'>
                     <%= t("footer.follow") %>
                 </span>
-                <% @user.user_social_links.each do |link| %>
                 <%=
                     link_to(
                         image_tag(
-                            link.social_link.logo,
+                            "social/email.svg",
                             class: "footer_social_icon",
-                            alt: link.social_link.i18n_link_title),
-                        link.generate_link,
-                        target: "_blank",
-                        title: link.social_link.i18n_link_title
+                            alt: t("social.mail")
+                        ),
+                        "javascript:showContactForm();",
+                        title: + t("index.email")
                     )
                 %>
-            <% end %>
+                <% @user.user_social_links.each do |link| %>
+                    <%=
+                        link_to(
+                            image_tag(
+                                link.social_link.logo,
+                                class: "footer_social_icon",
+                                alt: link.social_link.i18n_link_title
+                            ),
+                            link.generate_link,
+                            target: "_blank",
+                            title: link.social_link.i18n_link_title
+                        )
+                    %>
+                <% end %>
             </td>
             <td id='footer_center'>
                 <%= t("footer.copy", name: @user.full_name, year: Date.today.year) %>

+ 2 - 0
app/views/layouts/application.html.erb

@@ -7,6 +7,7 @@
         <%= csp_meta_tag %>
 
         <%= stylesheet_link_tag "ui", media: "all", "data-turbolinks-track": "reload" %>
+        <%= javascript_include_tag "main", "data-turbolinks-track" => true  %>
         <%= yield(:head) %>
     </head>
 
@@ -16,5 +17,6 @@
             <%= yield %>
         </main>
         <%= render "layouts/footer" %>
+        <%= render "layouts/contact_form"%>
   </body>
 </html>

+ 14 - 9
app/views/layouts/mailer.html.erb

@@ -1,13 +1,18 @@
 <!DOCTYPE html>
 <html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    <style>
-      /* Email styles need to be inline */
-    </style>
-  </head>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+        <style>
+            ul#details span{
+                font-weight: bold;
+            }
+            pre{
+                background-color: #333333;
+            }
+        </style>
+    </head>
 
-  <body>
-    <%= yield %>
-  </body>
+    <body>
+        <%= yield %>
+    </body>
 </html>

+ 2 - 3
app/views/main/index.html.erb

@@ -19,10 +19,9 @@
                     image_tag(
                         "social/email.svg", 
                         class: "footer_social_icon",
-                        alt: t("social.mail"),
-                        title: t("social.mail")
+                        alt: t("social.mail")
                     ) + t("index.email"),
-                    "mailto:" + @user.mail_adresses[0].address,
+                    "javascript:showContactForm()",
                     class: "a_button"
                 )
             %>

+ 14 - 0
config/application.rb

@@ -42,5 +42,19 @@ module InigoValentin
     ActiveStorage::Engine.config.active_storage.content_types_allowed_inline.append('text/html')
     print ActiveStorage::Engine.config.active_storage.content_types_to_serve_as_binary
     print ActiveStorage::Engine.config.active_storage.content_types_allowed_inline
+    
+    config.action_mailer.delivery_method = :smtp
+    config.action_mailer.smtp_settings = {
+       address:              Rails.application.credentials.smtp[:server],
+       port:                 Rails.application.credentials.smtp[:port],
+       domain:               Rails.application.credentials.smtp[:domain],
+       user_name:            Rails.application.credentials.smtp[:username],
+       password:             Rails.application.credentials.smtp[:password],
+       authentication:       Rails.application.credentials.smtp[:authentication],
+       #enable_starttls_auto: Rails.application.credentials.smtp[:enable_starttls_auto]
+       ssl:                  Rails.application.credentials.smtp[:ssl],
+       openssl_verify_mode:  Rails.application.credentials.smtp[:openssl_verify_mode]
+    }
+    
   end
 end

+ 1 - 1
config/environments/development.rb

@@ -34,7 +34,7 @@ Rails.application.configure do
   config.active_storage.service = :local
 
   # Don't care if the mailer can't send.
-  config.action_mailer.raise_delivery_errors = false
+  config.action_mailer.raise_delivery_errors = true
 
   config.action_mailer.perform_caching = false
 

+ 12 - 0
config/locales/en.yml

@@ -180,6 +180,18 @@ en:
         swift: "Swift"
         sqlite: "SQLite"
         android: "Android"
+    contact:
+        title: "Contact me"
+        name: "Your name"
+        mail: "Your eMail address"
+        message: "Message"
+        send: "Send"
+        cancel: "Cancel"
+        close: "Close"
+        confirmation: "The message has been sent!"
+        error:
+            invalid_email: "Invalid email"
+            empty_text: "The message is empty"
     help:
         expand: "Show text"
         about_title: "Sobre este sitio"

+ 12 - 0
config/locales/es.yml

@@ -180,6 +180,18 @@ es:
         swift: "Swift"
         sqlite: "SQLite"
         android: "Android"
+    contact:
+        title: "Contacto"
+        name: "Tu nombre"
+        mail: "Tu eMail"
+        message: "Mensaje"
+        send: "Enviar"
+        cancel: "Cancelar"
+        close: "Cerrar"
+        confirmation: "¡Mensaje enviado!"
+        error:
+            invalid_email: "eMail no válido"
+            empty_text: "El mensaje esta vacío"
     help:
         expand: "Mostrar texto"
         about_title: "Sobre este sitio"

+ 12 - 0
config/locales/eu.yml

@@ -180,6 +180,18 @@ eu:
         swift: "Swift"
         sqlite: "SQLite"
         android: "Android"
+    contact:
+        title: "Mesua bidali"
+        name: "Zure izena"
+        mail: "Zure eMail"
+        message: "Mesua"
+        send: "Bidali"
+        cancel: "Itxi"
+        close: "Itxi"
+        confirmation: "Mezua bidali da!"
+        error:
+            invalid_email: "eMail-a baliogabea da"
+            empty_text: "Mezua hutsik dago"
     help:
         expand: "Textua ikusi"
         about_title: "Webgune buruz"

+ 2 - 0
config/routes.rb

@@ -8,4 +8,6 @@ Rails.application.routes.draw do
     get "/:locale/projects", to: "projects#index", defaults: { locale: I18n.locale }, as: "project_index"
     get "/:locale/projects/:permalink", to: "projects#show", defaults: { locale: I18n.locale }, as: 'project_show'
     get "/:locale/help", to: "help#index", defaults: { locale: I18n.locale }, as: 'help'
+    get "/:locale/contact", to: "contact#index", defaults: { locale: I18n.locale }, as: 'contact'
+    post "/contact", to: "contact#create", defaults: { locale: I18n.locale }, as: 'contact_create'
 end

+ 15 - 0
db/migrate/20211228133200_create_messages.rb

@@ -0,0 +1,15 @@
+class CreateMessages < ActiveRecord::Migration[6.1]
+  def change
+    create_table :messages do |t|
+      t.references :user, null: false, foreign_key: true
+      t.string :sender_name
+      t.string :sender_email
+      t.text :text
+      t.datetime :dtime
+      t.string :language
+      t.string :ip
+
+      t.timestamps
+    end
+  end
+end

+ 15 - 1
db/schema.rb

@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2021_12_13_105552) do
+ActiveRecord::Schema.define(version: 2021_12_28_133200) do
 
   create_table "active_storage_attachments", charset: "utf8mb4", force: :cascade do |t|
     t.string "name", null: false
@@ -57,6 +57,19 @@ ActiveRecord::Schema.define(version: 2021_12_13_105552) do
     t.index ["user_id"], name: "index_mail_adresses_on_user_id"
   end
 
+  create_table "messages", charset: "utf8mb4", force: :cascade do |t|
+    t.bigint "user_id", null: false
+    t.string "sender_name"
+    t.string "sender_email"
+    t.text "text"
+    t.datetime "dtime"
+    t.string "language"
+    t.string "ip"
+    t.datetime "created_at", precision: 6, null: false
+    t.datetime "updated_at", precision: 6, null: false
+    t.index ["user_id"], name: "index_messages_on_user_id"
+  end
+
   create_table "project_urls", charset: "utf8mb4", force: :cascade do |t|
     t.bigint "project_id", null: false
     t.string "url"
@@ -183,6 +196,7 @@ ActiveRecord::Schema.define(version: 2021_12_13_105552) do
   add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
   add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
   add_foreign_key "mail_adresses", "users"
+  add_foreign_key "messages", "users"
   add_foreign_key "projects", "licenses"
   add_foreign_key "projects", "users"
   add_foreign_key "resumes", "users"

+ 7 - 0
test/controllers/contact_controller_test.rb

@@ -0,0 +1,7 @@
+require "test_helper"
+
+class ContactControllerTest < ActionDispatch::IntegrationTest
+  # test "the truth" do
+  #   assert true
+  # end
+end

+ 19 - 0
test/fixtures/messages.yml

@@ -0,0 +1,19 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+  user: one
+  sender_name: MyString
+  sender_email: MyString
+  text: MyText
+  dtime: 2021-12-28 14:32:00
+  language: MyString
+  ip: MyString
+
+two:
+  user: two
+  sender_name: MyString
+  sender_email: MyString
+  text: MyText
+  dtime: 2021-12-28 14:32:00
+  language: MyString
+  ip: MyString

+ 7 - 0
test/models/message_test.rb

@@ -0,0 +1,7 @@
+require "test_helper"
+
+class MessageTest < ActiveSupport::TestCase
+  # test "the truth" do
+  #   assert true
+  # end
+end