mirror of
https://github.com/typemill/typemill.git
synced 2025-08-05 13:47:37 +02:00
Version 2.1.0 add testmail feature
This commit is contained in:
61
system/typemill/Controllers/ControllerApiTestmail.php
Normal file
61
system/typemill/Controllers/ControllerApiTestmail.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Slim\Routing\RouteContext;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Models\SimpleMail;
|
||||
use Typemill\Static\Translations;
|
||||
|
||||
class ControllerApiTestmail extends Controller
|
||||
{
|
||||
public function send(Request $request, Response $response)
|
||||
{
|
||||
if(!isset($this->settings['mailfrom']) or !filter_var($this->settings['mailfrom'], FILTER_VALIDATE_EMAIL))
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => Translations::translate('The from mail is missing or it is not a valid e-mail address.')
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||||
}
|
||||
|
||||
$user = new User();
|
||||
$username = $request->getAttribute('c_username');
|
||||
|
||||
if(!$user->setUser($username))
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => Translations::translate('We did not find the a user or usermail.')
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||||
}
|
||||
|
||||
$userdata = $user->getUserData();
|
||||
|
||||
$mail = new SimpleMail($this->settings);
|
||||
|
||||
$subject = Translations::translate('Testmail from Typemill');
|
||||
$message = Translations::translate('This is a testmail from Typemill and if you read this e-mail, then everything works fine.');
|
||||
|
||||
$send = $mail->send($userdata['email'], $subject, $message);
|
||||
|
||||
if(!$send)
|
||||
{
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => Translations::translate('We could not send the testmail to your e-mail address. Reason: ') . $mail->error
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(500);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => Translations::translate('The testmail has been send, please check your inbox and your spam-folder to varify that you received the mail.')
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
@@ -25,7 +25,7 @@ class ControllerWebAuth extends Controller
|
||||
$input = $request->getParsedBody();
|
||||
$validation = new Validation();
|
||||
$securitylog = $this->settings['securitylog'] ?? false;
|
||||
$authcodeactive = $this->settings['authcode'] ?? false;
|
||||
$authcodeactive = $this->isAuthcodeActive($this->settings);
|
||||
$authtitle = Translations::translate('Verification code missing?');
|
||||
$authtext = Translations::translate('If you did not receive an email with the verification code, then the username or password you entered was wrong. Please try again.');
|
||||
|
||||
@@ -108,8 +108,6 @@ class ControllerWebAuth extends Controller
|
||||
|
||||
$send = $mail->send($userdata['email'], $subject, $message);
|
||||
|
||||
$send = true;
|
||||
|
||||
if(!$send)
|
||||
{
|
||||
$authtitle = Translations::translate('Error sending email');
|
||||
@@ -158,6 +156,21 @@ class ControllerWebAuth extends Controller
|
||||
}
|
||||
|
||||
|
||||
private function isAuthcodeActive($settings)
|
||||
{
|
||||
if(
|
||||
isset($settings['authcode']) &&
|
||||
$settings['authcode'] &&
|
||||
isset($settings['mailfrom']) &&
|
||||
filter_var($settings['mailfrom'], FILTER_VALIDATE_EMAIL)
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
# login a user with valid authcode
|
||||
public function loginWithAuthcode(Request $request, Response $response)
|
||||
{
|
||||
|
@@ -32,14 +32,13 @@ app.component('component-textarea', {
|
||||
<textarea rows="8" class="w-full border border-stone-300 text-stone-900 bg-stone-200 px-2 py-3"
|
||||
:id="id"
|
||||
:class="errors[name] ? ' border-red-500 bg-red-100' : ' border-stone-300 bg-stone-200'"
|
||||
:class="css"
|
||||
:readonly="readonly"
|
||||
:required="required"
|
||||
:disabled="disabled"
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)"></textarea>
|
||||
@input="update($event, name)"></textarea><slot></slot>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
</div>`,
|
||||
@@ -81,8 +80,7 @@ app.component('component-codearea', {
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)">
|
||||
</textarea>
|
||||
@input="update($event, name)"></textarea><slot></slot>
|
||||
<pre aria-hidden="true" class="highlight hljs"><code data-el="highlight" v-html="highlighted"></code></pre>
|
||||
</div>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
@@ -141,7 +139,7 @@ app.component('component-select', {
|
||||
@change="update($event,name)">
|
||||
<option disabled value="">Please select</option>
|
||||
<option v-for="option,optionkey in options" v-bind:value="optionkey">{{option}}</option>
|
||||
</select>
|
||||
</select><slot></slot>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
</div>`,
|
||||
@@ -173,7 +171,7 @@ app.component('component-checkbox', {
|
||||
v-model="checked"
|
||||
@change="update(checked, name)">
|
||||
<span class="ml-2 text-sm">{{ $filters.translate(checkboxlabel) }}</span>
|
||||
</label>
|
||||
</label><slot></slot>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
</div>`,
|
||||
@@ -209,7 +207,7 @@ app.component('component-checkboxlist', {
|
||||
v-model="checkedoptions"
|
||||
@change="update(checkedoptions, name)">
|
||||
<span class="ml-2 text-sm">{{ $filters.translate(option) }}</span>
|
||||
</label>
|
||||
</label><slot></slot>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
</div>`,
|
||||
@@ -249,7 +247,7 @@ app.component('component-radio', {
|
||||
v-model="picked"
|
||||
@change="update(picked, name)">
|
||||
<span class="ml-2 text-sm">{{ $filters.translate(option) }}</span>
|
||||
</label>
|
||||
</label><slot></slot>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
</div>`,
|
||||
@@ -277,7 +275,7 @@ app.component('component-number', {
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)">
|
||||
@input="update($event, name)"><slot></slot>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
</div>`,
|
||||
@@ -308,7 +306,7 @@ app.component('component-date', {
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)">
|
||||
@input="update($event, name)"><slot></slot>
|
||||
</div>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
@@ -342,7 +340,7 @@ app.component('component-email', {
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)">
|
||||
@input="update($event, name)"><slot></slot>
|
||||
</div>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
@@ -375,7 +373,7 @@ app.component('component-tel', {
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)">
|
||||
@input="update($event, name)"><slot></slot>
|
||||
</div>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
@@ -409,7 +407,7 @@ app.component('component-url', {
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)">
|
||||
@input="update($event, name)"><slot></slot>
|
||||
</div>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
@@ -442,7 +440,7 @@ app.component('component-color', {
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@input="update($event, name)">
|
||||
@input="update($event, name)"><slot></slot>
|
||||
</div>
|
||||
<p v-if="errors[name]" class="text-xs text-red-500">{{ errors[name] }}</p>
|
||||
<p v-else class="text-xs">{{ $filters.translate(description) }}</p>
|
||||
|
@@ -17,6 +17,13 @@ const app = Vue.createApp({
|
||||
:userroles="userroles"
|
||||
:value="formData[fieldname]"
|
||||
v-bind="subfieldDefinition">
|
||||
<slot v-if="fieldname == 'mailfrom'">
|
||||
<button
|
||||
class = "absolute px-2 py-3 ml-2 text-stone-50 bg-stone-700 hover:bg-stone-900 hover:text-white transition duration-100 cursor-pointer"
|
||||
style = "right:0px; width:200px;"
|
||||
@click.prevent = "testmail()"
|
||||
>send testmail</button>
|
||||
</slot>
|
||||
</component>
|
||||
</fieldset>
|
||||
</div>
|
||||
@@ -85,6 +92,32 @@ const app = Vue.createApp({
|
||||
this.currentTab = tab;
|
||||
this.reset();
|
||||
},
|
||||
testmail: function()
|
||||
{
|
||||
this.reset();
|
||||
var self = this;
|
||||
|
||||
tmaxios.post('/api/v1/testmail',{
|
||||
'url': data.urlinfo.route,
|
||||
})
|
||||
.then(function (response)
|
||||
{
|
||||
self.messageClass = 'bg-teal-500';
|
||||
self.message = response.data.message;
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
self.message = handleErrorMessage(error);
|
||||
self.messageClass = 'bg-rose-500';
|
||||
if(error.response.data.errors !== undefined)
|
||||
{
|
||||
self.errors = error.response.data.errors;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
save: function()
|
||||
{
|
||||
this.reset();
|
||||
@@ -109,7 +142,7 @@ const app = Vue.createApp({
|
||||
self.errors = error.response.data.errors;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
reset: function()
|
||||
{
|
||||
|
@@ -19,6 +19,7 @@ use Typemill\Controllers\ControllerApiAuthorArticle;
|
||||
use Typemill\Controllers\ControllerApiAuthorBlock;
|
||||
use Typemill\Controllers\ControllerApiAuthorMeta;
|
||||
use Typemill\Controllers\ControllerApiAuthorShortcode;
|
||||
use Typemill\Controllers\ControllerApiTestmail;
|
||||
|
||||
$app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
|
||||
@@ -35,6 +36,7 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
$group->post('/plugin', ControllerApiSystemPlugins::class . ':updatePlugin')->setName('api.plugin.set')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/extensions', ControllerApiSystemExtensions::class . ':activateExtension')->setName('api.extension.activate')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/versioncheck', ControllerApiSystemVersions::class . ':checkVersions')->setName('api.versioncheck')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->post('/testmail', ControllerApiTestmail::class . ':send')->setName('api.testmail')->add(new ApiAuthorization($acl, 'system', 'update')); # admin
|
||||
$group->get('/users/getbynames', ControllerApiSystemUsers::class . ':getUsersByNames')->setName('api.usersbynames')->add(new ApiAuthorization($acl, 'user', 'update')); # admin
|
||||
$group->get('/users/getbyemail', ControllerApiSystemUsers::class . ':getUsersByEmail')->setName('api.usersbyemail')->add(new ApiAuthorization($acl, 'user', 'update')); # admin
|
||||
$group->get('/users/getbyrole', ControllerApiSystemUsers::class . ':getUsersByRole')->setName('api.usersbyrole')->add(new ApiAuthorization($acl, 'user', 'update')); # admin
|
||||
|
@@ -164,22 +164,22 @@ fieldsetmail:
|
||||
legend: Email
|
||||
fields:
|
||||
mailfrom:
|
||||
type: text
|
||||
type: email
|
||||
label: 'Mail From (required)'
|
||||
placeholder: sender@yourmail.org
|
||||
maxlength: 60
|
||||
description: 'Enter an email address that should send emails (sender).'
|
||||
maxlength: 100
|
||||
description: 'Enter an email address that sends the e-mails (sender). The e-mail-feature will be used for recovery and verification e-mails. Send a testmail to your user-account to verify that you receive the e-mails.'
|
||||
mailfromname:
|
||||
type: text
|
||||
label: 'Mail From Name (optional)'
|
||||
placeholder: sender name
|
||||
maxlength: 60
|
||||
maxlength: 100
|
||||
description: 'Optionally enter a name for the sender address. If not set, the from-address will be visible.'
|
||||
replyto:
|
||||
type: text
|
||||
label: 'Reply To (optional)'
|
||||
placeholder: noreply@yourmail.org
|
||||
maxlength: 60
|
||||
maxlength: 100
|
||||
description: 'Optionally enter a "reply to" address for answers from the receiver. If not set, answers will go to the from-address.'
|
||||
fieldsetrecover:
|
||||
type: fieldset
|
||||
@@ -189,7 +189,7 @@ fieldsetrecover:
|
||||
type: checkbox
|
||||
label: 'Recover password'
|
||||
checkboxlabel: 'Activate a password recovery in the login form.'
|
||||
description: "Be aware that some providers might reject emails send with this feature (php-mail)."
|
||||
description: "From mail is required for this feature. Send a testmail before you use this feature."
|
||||
recoversubject:
|
||||
type: text
|
||||
label: 'Email subject'
|
||||
@@ -204,15 +204,11 @@ fieldsetsecurity:
|
||||
type: fieldset
|
||||
legend: Security
|
||||
fields:
|
||||
securitylog:
|
||||
type: checkbox
|
||||
label: 'Security log'
|
||||
checkboxlabel: 'Track spam and suspicious actions in a logfile'
|
||||
authcode:
|
||||
type: checkbox
|
||||
label: 'Login Verification (recommended)'
|
||||
checkboxlabel: 'Verify your login with a 5-digit code send by email.'
|
||||
description: 'Please test this with your account. If you do not get an email, make sure you have ftp-access to disable the feature in the settings.yaml manually. The verification code will be valid for 5 minutes. Be aware that device fingerprints will be stored in the user accounts. Make sure this complies with privacy legislation in your country.'
|
||||
description: 'From mail is required for this feature. Send a testmail before you use this feature. Make sure you have ftp-access to disable the feature in settings.yaml on failure. The verification code will be valid for 5 minutes. Be aware that device fingerprints will be stored in the user accounts.'
|
||||
authcaptcha:
|
||||
type: radio
|
||||
label: 'Use captcha in authentication forms'
|
||||
@@ -220,6 +216,10 @@ fieldsetsecurity:
|
||||
disabled: 'Disable'
|
||||
standard: 'Always show'
|
||||
aftererror: 'Show after first wrong input'
|
||||
securitylog:
|
||||
type: checkbox
|
||||
label: 'Security log'
|
||||
checkboxlabel: 'Track spam and suspicious actions in a logfile'
|
||||
fieldsetdeveloper:
|
||||
type: fieldset
|
||||
legend: "Developer"
|
||||
@@ -242,18 +242,19 @@ fieldsetdeveloper:
|
||||
headersoff:
|
||||
type: checkbox
|
||||
label: "Disable Custom Headers"
|
||||
checkboxlabel: "Disable all custom headers of Typemill and send your own headers instead."
|
||||
corsdomains:
|
||||
type: textarea
|
||||
label: "Allowed Domains for API-Access (CORS)"
|
||||
placeholder: 'https://my-website-that-uses-the-api.org,https://another-website-using-the-api.org'
|
||||
description: "Add all domains separated with comma, that should have access to the Typemill API. Domains will be added to the cors-header."
|
||||
cspdomains:
|
||||
type: textarea
|
||||
label: "Allowed Domains for Content on Typemill (CSP)"
|
||||
placeholder: 'https://www.google.com,*google.com'
|
||||
description: "Add all domains separated with comma, that you want to integrate on your Typemill website. Domains will be added to the csp-header. Usually done with plugins and themes, but add manually if something is blocked."
|
||||
checkboxlabel: "Disable all custom headers of Typemill (except cors) and send your own headers instead."
|
||||
cspdisabled:
|
||||
type: checkbox
|
||||
label: "Disable CSP Headers"
|
||||
checkboxlabel: "Disable all csp (content security policy) headers for this website."
|
||||
checkboxlabel: "Disable all csp headers (content security policy) for this website."
|
||||
cspdomains:
|
||||
type: textarea
|
||||
label: "Allowed Domains for Content on Typemill (CSP-Headers)"
|
||||
placeholder: 'https://www.google.com,*google.com'
|
||||
description: "List all domains, separated by commas, to allow content integration, such as iframes, on your Typemill website. Domains will be added to the csp-header. Usually done with plugins and themes, but add manually if something is blocked."
|
||||
corsdomains:
|
||||
type: textarea
|
||||
label: "Allowed Domains for API-Access (CORS-Headers)"
|
||||
placeholder: 'https://my-website-that-uses-the-api.org,https://another-website-using-the-api.org'
|
||||
description: "List all domains, separated by comma, that should have access to the Typemill API. Domains will be added to the cors-header."
|
||||
|
Reference in New Issue
Block a user