de">
@@ -0,0 +1,29 @@
1
+from abc import ABC, abstractmethod
2
+
3
+class PostRepository(ABC):
4
+    
5
+    @abstractmethod
6
+    def list(self, page=1, per_page=10, order_by="-created"): pass
7
+    
8
+    @abstractmethod
9
+    def add(self, post): pass
10
+
11
+    @abstractmethod
12
+    def get(self, post_id): pass
13
+
14
+    @abstractmethod
15
+    def update(self, post):
16
+        """Update an existing post."""
17
+        pass
18
+
19
+    @abstractmethod
20
+    def delete(self, post_id):
21
+        """Delete a post by its ID."""
22
+        pass
23
+
24
+
25
+    @abstractmethod
26
+    def search(self, query: str): pass
27
+
28
+    @abstractmethod
29
+    def rate_post(self, post_id, user_id, rating: int): pass

+ 11 - 0
commu/interfaces/user_repository.py

@@ -0,0 +1,11 @@
1
+from abc import ABC, abstractmethod
2
+
3
+class UserRepository(ABC):
4
+    @abstractmethod
5
+    def add(self, user): pass
6
+
7
+    @abstractmethod
8
+    def get(self, user_id): pass
9
+
10
+    @abstractmethod
11
+    def find_by_username(self, username): pass

+ 0 - 0
commu/tests/__init__.py


BIN
commu/tests/__pycache__/__init__.cpython-310.pyc


BIN
commu/tests/__pycache__/__init__.cpython-311.pyc


BIN
commu/tests/__pycache__/test_post_repository.cpython-310-pytest-8.4.1.pyc


BIN
commu/tests/__pycache__/test_post_repository.cpython-311.pyc


BIN
commu/tests/__pycache__/test_post_service.cpython-310-pytest-8.4.1.pyc


BIN
commu/tests/__pycache__/test_post_service.cpython-311.pyc


BIN
commu/tests/__pycache__/test_user_service.cpython-310-pytest-8.4.1.pyc


BIN
commu/tests/__pycache__/test_user_service.cpython-311.pyc


+ 27 - 0
commu/tests/test_post_repository.py

@@ -0,0 +1,27 @@
1
+from commu.infrastructure.memory_post_repository import InMemoryPostRepository
2
+from commu.entities.post import Post
3
+
4
+def test_add_and_get_post():
5
+    repo = InMemoryPostRepository()
6
+    post = Post("user1", "content", images=["img.png"])
7
+    repo.add(post)
8
+    assert repo.get(post.id) == post
9
+
10
+def test_search():
11
+    repo = InMemoryPostRepository()
12
+    p1 = Post("u", "hello apple")
13
+    p2 = Post("u", "banana orange")
14
+    repo.add(p1)
15
+    repo.add(p2)
16
+    results = repo.search("apple")
17
+    assert len(results) == 1 and results[0].id == p1.id
18
+
19
+def test_rate_post():
20
+    repo = InMemoryPostRepository()
21
+    p = Post("u", "rate me")
22
+    repo.add(p)
23
+    repo.rate_post(p.id, "userA", 3)
24
+    repo.rate_post(p.id, "userB", 5)
25
+    # userA updates rating
26
+    repo.rate_post(p.id, "userA", 4)
27
+    assert p.ratings == [("userB", 5), ("userA", 4)] or p.ratings == [("userA", 4), ("userB", 5)]

+ 57 - 0
commu/tests/test_post_service.py

@@ -0,0 +1,57 @@
1
+import pytest
2
+from commu.usecases.post_service import PostService
3
+from commu.usecases.user_service import UserService
4
+from commu.infrastructure.memory_user_repository import InMemoryUserRepository
5
+from commu.infrastructure.memory_post_repository import InMemoryPostRepository
6
+from commu.entities.user import UserRole
7
+
8
+@pytest.fixture
9
+def setup_services():
10
+    user_repo = InMemoryUserRepository()
11
+    post_repo = InMemoryPostRepository()
12
+    user_svc = UserService(user_repo)
13
+    post_svc = PostService(post_repo, user_repo)
14
+    admin = user_svc.register("admin", UserRole.ADMIN)
15
+    user = user_svc.register("user", UserRole.USER)
16
+    guest = user_svc.register("guest", UserRole.GUEST)
17
+    return user_svc, post_svc, admin, user, guest
18
+
19
+def test_create_post(setup_services):
20
+    user_svc, post_svc, admin, user, guest = setup_services
21
+    post = post_svc.create_post(user.id, "My post", images=["a.jpg"])
22
+    assert post.text == "My post"
23
+    assert post.images == ["a.jpg"]
24
+
25
+def test_guest_cannot_create_post(setup_services):
26
+    user_svc, post_svc, admin, user, guest = setup_services
27
+    with pytest.raises(PermissionError):
28
+        post_svc.create_post(guest.id, "forbidden")
29
+
30
+def test_search_posts(setup_services):
31
+    user_svc, post_svc, admin, user, guest = setup_services
32
+    post1 = post_svc.create_post(user.id, "Hello world")
33
+    post2 = post_svc.create_post(user.id, "Another post")
34
+    results = post_svc.search_posts("hello")
35
+    assert len(results) == 1
36
+    assert results[0].text == "Hello world"
37
+
38
+def test_rate_post(setup_services):
39
+    user_svc, post_svc, admin, user, guest = setup_services
40
+    post = post_svc.create_post(user.id, "My post")
41
+    post_svc.rate_post(post.id, admin.id, 5)
42
+    post_svc.rate_post(post.id, user.id, 4)
43
+    assert abs(post.average_rating() - 4.5) < 1e-6
44
+
45
+def test_guest_cannot_rate(setup_services):
46
+    user_svc, post_svc, admin, user, guest = setup_services
47
+    post = post_svc.create_post(user.id, "Guest can't rate")
48
+    with pytest.raises(PermissionError):
49
+        post_svc.rate_post(post.id, guest.id, 5)
50
+
51
+def test_rating_bounds(setup_services):
52
+    user_svc, post_svc, admin, user, guest = setup_services
53
+    post = post_svc.create_post(user.id, "Test bounds")
54
+    with pytest.raises(ValueError):
55
+        post_svc.rate_post(post.id, admin.id, 0)
56
+    with pytest.raises(ValueError):
57
+        post_svc.rate_post(post.id, admin.id, 6)

+ 25 - 0
commu/tests/test_user_service.py

@@ -0,0 +1,25 @@
1
+import pytest
2
+from commu.usecases.user_service import UserService
3
+from commu.infrastructure.memory_user_repository import InMemoryUserRepository
4
+from commu.entities.user import UserRole
5
+
6
+def test_register_user():
7
+    repo = InMemoryUserRepository()
8
+    svc = UserService(repo)
9
+    user = svc.register("testuser")
10
+    assert user.username == "testuser"
11
+    assert user.role == UserRole.USER
12
+    assert repo.get(user.id) == user
13
+
14
+def test_register_admin():
15
+    repo = InMemoryUserRepository()
16
+    svc = UserService(repo)
17
+    user = svc.register("adminuser", UserRole.ADMIN)
18
+    assert user.role == UserRole.ADMIN
19
+
20
+def test_get_user():
21
+    repo = InMemoryUserRepository()
22
+    svc = UserService(repo)
23
+    user = svc.register("abc")
24
+    found = svc.get_user(user.id)
25
+    assert found.username == "abc"

+ 0 - 0
commu/usecases/__init__.py


BIN
commu/usecases/__pycache__/__init__.cpython-310.pyc


BIN
commu/usecases/__pycache__/__init__.cpython-311.pyc


BIN
commu/usecases/__pycache__/post_service.cpython-310.pyc


BIN
commu/usecases/__pycache__/post_service.cpython-311.pyc


BIN
commu/usecases/__pycache__/user_service.cpython-310.pyc


BIN
commu/usecases/__pycache__/user_service.cpython-311.pyc


+ 28 - 0
commu/usecases/post_service.py

@@ -0,0 +1,28 @@
1
+from commu.entities.post import Post
2
+from commu.entities.user import UserRole
3
+
4
+class PostService:
5
+    def __init__(self, post_repo, user_repo):
6
+        self.post_repo = post_repo
7
+        self.user_repo = user_repo
8
+
9
+    def create_post(self, author_id, text, images=None, videos=None):
10
+        user = self.user_repo.get(author_id)
11
+        print(f"user = {user}")
12
+        print(f"role = {user.role}")
13
+        if not user or user.role == UserRole.GUEST:
14
+            raise PermissionError("User does not have permission to create posts")
15
+        post = Post(author_id, text, images, videos)
16
+        self.post_repo.add(post)
17
+        return post
18
+
19
+    def search_posts(self, query):
20
+        return self.post_repo.search(query)
21
+
22
+    def rate_post(self, post_id, user_id, rating):
23
+        user = self.user_repo.get(user_id)
24
+        if not user or user.role == UserRole.GUEST:
25
+            raise PermissionError("Guests cannot rate posts")
26
+        if not (1 <= rating <= 5):
27
+            raise ValueError("Rating must be between 1 and 5")
28
+        self.post_repo.rate_post(post_id, user_id, rating)

+ 13 - 0
commu/usecases/user_service.py

@@ -0,0 +1,13 @@
1
+from commu.entities.user import User, UserRole
2
+
3
+class UserService:
4
+    def __init__(self, user_repo):
5
+        self.user_repo = user_repo
6
+
7
+    def register(self, username, role=UserRole.USER):
8
+        user = User(username, role)
9
+        self.user_repo.add(user)
10
+        return user
11
+
12
+    def get_user(self, user_id):
13
+        return self.user_repo.get(user_id)

+ 0 - 0
core/__init__.py


BIN
core/__pycache__/__init__.cpython-311.pyc


BIN
core/__pycache__/admin.cpython-311.pyc


BIN
core/__pycache__/apps.cpython-311.pyc


BIN
core/__pycache__/models.cpython-311.pyc


BIN
core/__pycache__/tests.cpython-311.pyc


BIN
core/__pycache__/urls.cpython-311.pyc


BIN
core/__pycache__/views.cpython-311.pyc


+ 3 - 0
core/admin.py

@@ -0,0 +1,3 @@
1
+from django.contrib import admin
2
+
3
+# Register your models here.

+ 6 - 0
core/apps.py

@@ -0,0 +1,6 @@
1
+from django.apps import AppConfig
2
+
3
+
4
+class CoreConfig(AppConfig):
5
+    default_auto_field = 'django.db.models.BigAutoField'
6
+    name = 'core'

+ 38 - 0
core/migrations/0001_initial.py

@@ -0,0 +1,38 @@
1
+# Generated by Django 5.2.4 on 2025-08-03 16:36
2
+
3
+import django.db.models.deletion
4
+from django.conf import settings
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    initial = True
11
+
12
+    dependencies = [
13
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14
+    ]
15
+
16
+    operations = [
17
+        migrations.CreateModel(
18
+            name='Post',
19
+            fields=[
20
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+                ('text', models.TextField()),
22
+                ('images', models.JSONField(blank=True, default=list)),
23
+                ('videos', models.JSONField(blank=True, default=list)),
24
+                ('created', models.DateTimeField(auto_now_add=True)),
25
+                ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='posts', to=settings.AUTH_USER_MODEL)),
26
+            ],
27
+        ),
28
+        migrations.CreateModel(
29
+            name='PostRating',
30
+            fields=[
31
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
32
+                ('rating', models.IntegerField()),
33
+                ('created', models.DateTimeField(auto_now_add=True)),
34
+                ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='core.post')),
35
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
36
+            ],
37
+        ),
38
+    ]

+ 0 - 0
core/migrations/__init__.py


BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc


BIN
core/migrations/__pycache__/__init__.cpython-311.pyc


+ 18 - 0
core/models.py

@@ -0,0 +1,18 @@
1
+from django.db import models
2
+from django.contrib.auth import get_user_model
3
+
4
+User = get_user_model()
5
+
6
+class Post(models.Model):
7
+    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
8
+    text = models.TextField()
9
+    images = models.JSONField(default=list, blank=True)
10
+    videos = models.JSONField(default=list, blank=True)
11
+    created = models.DateTimeField(auto_now_add=True)
12
+
13
+class PostRating(models.Model):
14
+    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="ratings")
15
+    user = models.ForeignKey(User, on_delete=models.CASCADE)
16
+    rating = models.IntegerField()
17
+    created = models.DateTimeField(auto_now_add=True)
18
+

BIN
core/repositories/__pycache__/django_post_repository.cpython-311.pyc


BIN
core/repositories/__pycache__/django_user_repository.cpython-311.pyc


+ 112 - 0
core/repositories/django_post_repository.py

@@ -0,0 +1,112 @@
1
+from commu.interfaces.post_repository import PostRepository
2
+from core.models import Post, PostRating
3
+from django.contrib.auth import get_user_model
4
+
5
+User = get_user_model()
6
+
7
+class DjangoPostRepository(PostRepository):
8
+    def add(self, post):
9
+        # post: commu.entities.post.Post
10
+        try:
11
+            author = User.objects.get(id=post.author_id)
12
+        except User.DoesNotExist:
13
+            raise ValueError("Author not found")
14
+        obj = Post.objects.create(
15
+            author=author,
16
+            text=post.text,
17
+            images=post.images,
18
+            videos=post.videos,
19
+        )
20
+        post.id = str(obj.id)
21
+        return post
22
+
23
+    def get(self, post_id):
24
+        try:
25
+            obj = Post.objects.get(id=post_id)
26
+            from commu.entities.post import Post as DomainPost
27
+            domain_post = DomainPost(
28
+                author_id=str(obj.author.id),
29
+                text=obj.text,
30
+                images=obj.images,
31
+                videos=obj.videos,
32
+            )
33
+            domain_post.id = str(obj.id)
34
+            # Load ratings
35
+            domain_post.ratings = [
36
+                (str(r.user.id), r.rating)
37
+                for r in obj.ratings.all()
38
+            ]
39
+            return domain_post
40
+        except Post.DoesNotExist:
41
+            return None
42
+
43
+    def search(self, query: str):
44
+        qs = Post.objects.filter(text__icontains=query)
45
+        from commu.entities.post import Post as DomainPost
46
+        result = []
47
+        for obj in qs:
48
+            domain_post = DomainPost(
49
+                author_id=str(obj.author.id),
50
+                text=obj.text,
51
+                images=obj.images,
52
+                videos=obj.videos,
53
+            )
54
+            domain_post.id = str(obj.id)
55
+            # Load ratings
56
+            domain_post.ratings = [
57
+                (str(r.user.id), r.rating)
58
+                for r in obj.ratings.all()
59
+            ]
60
+            result.append(domain_post)
61
+        return result
62
+
63
+    def rate_post(self, post_id, user_id, rating: int):
64
+        try:
65
+            post = Post.objects.get(id=post_id)
66
+            user = User.objects.get(id=user_id)
67
+        except (Post.DoesNotExist, User.DoesNotExist):
68
+            raise ValueError("Post or user not found")
69
+        # Remove old rating from user, if any
70
+        PostRating.objects.filter(post=post, user=user).delete()
71
+        # Create new rating
72
+        PostRating.objects.create(post=post, user=user, rating=rating)
73
+
74
+    def delete(self, post_id):
75
+        try:
76
+            obj = Post.objects.get(id=post_id)
77
+            obj.delete()
78
+        except Post.DoesNotExist:
79
+            raise ValueError("Post not found")
80
+
81
+    def list(self, page=1, per_page=10, order_by="-created"):
82
+        qs = Post.objects.all().order_by(order_by)
83
+        total = qs.count()
84
+        start = (page - 1) * per_page
85
+        end = start + per_page
86
+        from commu.entities.post import Post as DomainPost
87
+        posts = []
88
+        for obj in qs[start:end]:
89
+            domain_post = DomainPost(
90
+                author_id=str(obj.author.id),
91
+                text=obj.text,
92
+                images=obj.images,
93
+                videos=obj.videos,
94
+            )
95
+            domain_post.id = str(obj.id)
96
+            domain_post.ratings = [
97
+                (str(r.user.id), r.rating)
98
+                for r in obj.ratings.all()
99
+            ]
100
+            posts.append(domain_post)
101
+        return posts, total
102
+
103
+    def update(self, post):
104
+        try:
105
+            obj = Post.objects.get(id=post.id)
106
+            obj.text = post.text
107
+            obj.images = post.images
108
+            obj.videos = post.videos
109
+            obj.save()
110
+            # Optionally update ratings (if part of update)
111
+        except Post.DoesNotExist:
112
+            raise ValueError("Post not found")

+ 55 - 0
core/repositories/django_user_repository.py

@@ -0,0 +1,55 @@
1
+from commu.interfaces.user_repository import UserRepository
2
+from django.contrib.auth.models import User as DjangoUser
3
+
4
+class DjangoUserRepository(UserRepository):
5
+    def add(self, user):
6
+        # user: commu.entities.user.User
7
+        # Map role to Django flags
8
+        is_staff = user.role.value in ("admin", "user") if hasattr(user.role, "value") else user.role in ("admin", "user")
9
+        is_superuser = user.role.value == "admin" if hasattr(user.role, "value") else user.role == "admin"
10
+        obj = DjangoUser.objects.create_user(
11
+            username=user.username,
12
+            password=None,  # You may set/generate a password as needed!
13
+            is_staff=is_staff,
14
+            is_superuser=is_superuser,
15
+        )
16
+        user.id = str(obj.id)
17
+        return user
18
+
19
+    def get(self, user_id):
20
+        try:
21
+            obj = DjangoUser.objects.get(id=user_id)
22
+            from commu.entities.user import User, UserRole
23
+            role = self._role_from_django(obj)
24
+            u = User(
25
+                username=obj.username,
26
+                role=role,
27
+            )
28
+            u.id = str(obj.id)
29
+            return u
30
+        except DjangoUser.DoesNotExist:
31
+            return None
32
+
33
+    def find_by_username(self, username):
34
+        try:
35
+            obj = DjangoUser.objects.get(username=username)
36
+            from commu.entities.user import User, UserRole
37
+            role = self._role_from_django(obj)
38
+            u = User(
39
+                username=obj.username,
40
+                role=role,
41
+            )
42
+            u.id = str(obj.id)
43
+            return u
44
+        except DjangoUser.DoesNotExist:
45
+            return None
46
+
47
+    def _role_from_django(self, user_obj):
48
+        from commu.entities.user import UserRole
49
+        if user_obj.is_superuser:
50
+            return UserRole.ADMIN
51
+        elif user_obj.is_staff:
52
+            return UserRole.USER
53
+        else:
54
+            return UserRole.GUEST
55
+

+ 3 - 0
core/tests.py

@@ -0,0 +1,3 @@
1
+from django.test import TestCase
2
+
3
+# Create your tests here.

+ 0 - 0
core/tests/__init__.py


BIN
core/tests/__pycache__/__init__.cpython-311.pyc


BIN
core/tests/__pycache__/test_post_service_django.cpython-311.pyc


+ 60 - 0
core/tests/test_post_service_django.py

@@ -0,0 +1,60 @@
1
+import pytest
2
+from django.test import TestCase
3
+from django.contrib.auth import get_user_model
4
+
5
+from commu.usecases.post_service import PostService
6
+from commu.entities.user import UserRole
7
+from core.repositories.django_post_repository import DjangoPostRepository
8
+from core.repositories.django_user_repository import DjangoUserRepository
9
+
10
+User = get_user_model()
11
+
12
+class PostServiceIntegrationTest(TestCase):
13
+
14
+    def setUp(self):
15
+        # Use Django's repositories
16
+        self.user_repo = DjangoUserRepository()
17
+        self.post_repo = DjangoPostRepository()
18
+        self.service = PostService(self.post_repo, self.user_repo)
19
+
20
+        # Create some users using Django ORM
21
+        self.admin = User.objects.create_user(username="admin", is_superuser=True, is_staff=True)
22
+        self.user = User.objects.create_user(username="user", is_staff=True)
23
+        self.guest = User.objects.create_user(username="guest")  # Not staff or superuser
24
+
25
+    def test_create_post_by_user(self):
26
+        post = self.service.create_post(str(self.user.id), "Hello Django!", ["img1.png"], ["vid1.mp4"])
27
+        assert post.text == "Hello Django!"
28
+        assert post.images == ["img1.png"]
29
+        assert post.videos == ["vid1.mp4"]
30
+
31
+    def test_guest_cannot_create_post(self):
32
+        with self.assertRaises(PermissionError):
33
+            self.service.create_post(str(self.guest.id), "Forbidden")
34
+
35
+    def test_search_posts(self):
36
+        p1 = self.service.create_post(str(self.user.id), "First Django post")
37
+        p2 = self.service.create_post(str(self.user.id), "Second hello world")
38
+        results = self.service.search_posts("hello")
39
+        assert len(results) == 1
40
+        assert results[0].text == "Second hello world"
41
+
42
+    def test_rate_post(self):
43
+        post = self.service.create_post(str(self.user.id), "Rate me Django!")
44
+        self.service.rate_post(post.id, str(self.admin.id), 5)
45
+        self.service.rate_post(post.id, str(self.user.id), 4)
46
+        updated_post = self.post_repo.get(post.id)
47
+        assert abs(updated_post.average_rating() - 4.5) < 1e-6
48
+
49
+    def test_guest_cannot_rate_post(self):
50
+        post = self.service.create_post(str(self.user.id), "No guest rate")
51
+        with self.assertRaises(PermissionError):
52
+            self.service.rate_post(post.id, str(self.guest.id), 2)
53
+
54
+    def test_rating_bounds(self):
55
+        post = self.service.create_post(str(self.user.id), "Bounds Django")
56
+        with self.assertRaises(ValueError):
57
+            self.service.rate_post(post.id, str(self.admin.id), 0)
58
+        with self.assertRaises(ValueError):
59
+            self.service.rate_post(post.id, str(self.admin.id), 6)
60
+

+ 8 - 0
core/urls.py

@@ -0,0 +1,8 @@
1
+from django.urls import path
2
+from . import views
3
+
4
+urlpatterns = [
5
+    path('posts/create/', views.create_post, name='create_post'),
6
+    path('posts/search/', views.search_posts, name='search_posts'),
7
+    path('posts/rate/', views.rate_post, name='rate_post'),
8
+]

+ 69 - 0
core/views.py

@@ -0,0 +1,69 @@
1
+import json
2
+from django.http import JsonResponse, HttpResponseBadRequest
3
+from django.views.decorators.csrf import csrf_exempt
4
+from django.contrib.auth.decorators import login_required
5
+from commu.usecases.post_service import PostService
6
+from core.repositories.django_post_repository import DjangoPostRepository
7
+from core.repositories.django_user_repository import DjangoUserRepository
8
+
9
+# Service setup (could be moved to app config for larger apps)
10
+user_repo = DjangoUserRepository()
11
+post_repo = DjangoPostRepository()
12
+post_service = PostService(post_repo, user_repo)
13
+
14
+@csrf_exempt
15
+@login_required
16
+def create_post(request):
17
+    if request.method != 'POST':
18
+        return HttpResponseBadRequest("POST only")
19
+    try:
20
+        data = json.loads(request.body.decode())
21
+        text = data.get("text")
22
+        images = data.get("images", [])
23
+        videos = data.get("videos", [])
24
+        if not text:
25
+            return JsonResponse({"error": "Missing post text"}, status=400)
26
+        # Use logged-in Django user as author
27
+        user_id = str(request.user.id)
28
+        post = post_service.create_post(user_id, text, images, videos)
29
+        return JsonResponse({
30
+            "id": post.id,
31
+            "text": post.text,
32
+            "author_id": post.author_id,
33
+            "images": post.images,
34
+            "videos": post.videos
35
+        })
36
+    except Exception as e:
37
+        return JsonResponse({"error": str(e)}, status=400)
38
+
39
+def search_posts(request):
40
+    q = request.GET.get("q", "")
41
+    results = post_service.search_posts(q)
42
+    posts = [{
43
+        "id": p.id,
44
+        "text": p.text,
45
+        "author_id": p.author_id,
46
+        "images": p.images,
47
+        "videos": p.videos,
48
+        "avg_rating": p.average_rating()
49
+    } for p in results]
50
+    return JsonResponse({"results": posts})
51
+
52
+@csrf_exempt
53
+@login_required
54
+def rate_post(request):
55
+    if request.method != 'POST':
56
+        return HttpResponseBadRequest("POST only")
57
+    try:
58
+        data = json.loads(request.body.decode())
59
+        post_id = data.get("post_id")
60
+        rating = data.get("rating")
61
+        if not post_id or rating is None:
62
+            return JsonResponse({"error": "Missing post_id or rating"}, status=400)
63
+        user_id = str(request.user.id)
64
+        post_service.rate_post(post_id, user_id, int(rating))
65
+        return JsonResponse({"ok": True})
66
+    except Exception as e:
67
+        return JsonResponse({"error": str(e)}, status=400)
68
+
69
+

+ 0 - 0
db.sqlite3


+ 0 - 0
django_commu/__init__.py


BIN
django_commu/__pycache__/__init__.cpython-311.pyc


BIN
django_commu/__pycache__/settings.cpython-311.pyc


BIN
django_commu/__pycache__/urls.cpython-311.pyc


BIN
django_commu/__pycache__/wsgi.cpython-311.pyc


+ 16 - 0
django_commu/asgi.py

@@ -0,0 +1,16 @@
1
+"""
2
+ASGI config for django_commu project.
3
+
4
+It exposes the ASGI callable as a module-level variable named ``application``.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
8
+"""
9
+
10
+import os
11
+
12
+from django.core.asgi import get_asgi_application
13
+
14
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_commu.settings')
15
+
16
+application = get_asgi_application()

+ 128 - 0
django_commu/settings.py

@@ -0,0 +1,128 @@
1
+"""
2
+Django settings for django_commu project.
3
+
4
+Generated by 'django-admin startproject' using Django 5.2.4.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/5.2/topics/settings/
8
+
9
+For the full list of settings and their values, see
10
+https://docs.djangoproject.com/en/5.2/ref/settings/
11
+"""
12
+
13
+from pathlib import Path
14
+import os
15
+
16
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
17
+BASE_DIR = Path(__file__).resolve().parent.parent
18
+
19
+
20
+# Quick-start development settings - unsuitable for production
21
+# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
22
+
23
+# SECURITY WARNING: keep the secret key used in production secret!
24
+SECRET_KEY = 'django-insecure-c$ieo22nwe^v#*e$xlh@fath9*t+f8wtqrt9e!op9-ittc=b_x'
25
+
26
+# SECURITY WARNING: don't run with debug turned on in production!
27
+DEBUG = True
28
+
29
+ALLOWED_HOSTS = []
30
+
31
+
32
+# Application definition
33
+
34
+INSTALLED_APPS = [
35
+    'django.contrib.admin',
36
+    'django.contrib.auth',
37
+    'django.contrib.contenttypes',
38
+    'django.contrib.sessions',
39
+    'django.contrib.messages',
40
+    'django.contrib.staticfiles',
41
+    'core',
42
+]
43
+
44
+MIDDLEWARE = [
45
+    'django.middleware.security.SecurityMiddleware',
46
+    'django.contrib.sessions.middleware.SessionMiddleware',
47
+    'django.middleware.common.CommonMiddleware',
48
+    'django.middleware.csrf.CsrfViewMiddleware',
49
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
50
+    'django.contrib.messages.middleware.MessageMiddleware',
51
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
52
+]
53
+
54
+ROOT_URLCONF = 'django_commu.urls'
55
+
56
+TEMPLATES = [
57
+    {
58
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
59
+        'DIRS': [],
60
+        'APP_DIRS': True,
61
+        'OPTIONS': {
62
+            'context_processors': [
63
+                'django.template.context_processors.request',
64
+                'django.contrib.auth.context_processors.auth',
65
+                'django.contrib.messages.context_processors.messages',
66
+            ],
67
+        },
68
+    },
69
+]
70
+
71
+WSGI_APPLICATION = 'django_commu.wsgi.application'
72
+
73
+
74
+# Database
75
+# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
76
+
77
+DATABASES = {
78
+    'default': {
79
+        'ENGINE': 'django.db.backends.postgresql',
80
+        'NAME': os.getenv('DB_NAME', 'commudb'),
81
+        'USER': os.getenv('DB_USER', 'commuuser'),
82
+        'PASSWORD': os.getenv('DB_PASSWORD', 'commupass'),
83
+        'HOST': os.getenv('DB_HOST', 'db'),
84
+        'PORT': os.getenv('DB_PORT', '5432'),
85
+    }
86
+}
87
+
88
+
89
+# Password validation
90
+# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
91
+
92
+AUTH_PASSWORD_VALIDATORS = [
93
+    {
94
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
95
+    },
96
+    {
97
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
98
+    },
99
+    {
100
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
101
+    },
102
+    {
103
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
104
+    },
105
+]
106
+
107
+
108
+# Internationalization
109
+# https://docs.djangoproject.com/en/5.2/topics/i18n/
110
+
111
+LANGUAGE_CODE = 'en-us'
112
+
113
+TIME_ZONE = 'UTC'
114
+
115
+USE_I18N = True
116
+
117
+USE_TZ = True
118
+
119
+
120
+# Static files (CSS, JavaScript, Images)
121
+# https://docs.djangoproject.com/en/5.2/howto/static-files/
122
+
123
+STATIC_URL = 'static/'
124
+
125
+# Default primary key field type
126
+# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
127
+
128
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

+ 23 - 0
django_commu/urls.py

@@ -0,0 +1,23 @@
1
+"""
2
+URL configuration for django_commu project.
3
+
4
+The `urlpatterns` list routes URLs to views. For more information please see:
5
+    https://docs.djangoproject.com/en/5.2/topics/http/urls/
6
+Examples:
7
+Function views
8
+    1. Add an import:  from my_app import views
9
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
10
+Class-based views
11
+    1. Add an import:  from other_app.views import Home
12
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
13
+Including another URLconf
14
+    1. Import the include() function: from django.urls import include, path
15
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
16
+"""
17
+from django.contrib import admin
18
+from django.urls import path, include
19
+
20
+urlpatterns = [
21
+    path('admin/', admin.site.urls),
22
+    path('core/', include('core.urls')),
23
+]

+ 16 - 0
django_commu/wsgi.py

@@ -0,0 +1,16 @@
1
+"""
2
+WSGI config for django_commu project.
3
+
4
+It exposes the WSGI callable as a module-level variable named ``application``.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
8
+"""
9
+
10
+import os
11
+
12
+from django.core.wsgi import get_wsgi_application
13
+
14
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_commu.settings')
15
+
16
+application = get_wsgi_application()

+ 40 - 0
docker-compose.yml

@@ -0,0 +1,40 @@
1
+version: '3.9'
2
+
3
+services:
4
+  db:
5
+    image: postgres:16
6
+    environment:
7
+      POSTGRES_DB: commudb
8
+      POSTGRES_USER: commuuser
9
+      POSTGRES_PASSWORD: commupass
10
+    ports:
11
+      - "5432:5432"
12
+    volumes:
13
+      - postgres_data:/var/lib/postgresql/data
14
+
15
+  web:
16
+    build:
17
+      context: .
18
+      args:
19
+        UID: ${UID}
20
+        GID: ${GID}
21
+    user: "${UID}:${GID}"
22
+    command: >
23
+      sh -c "python manage.py runserver 0.0.0.0:8000"
24
+    volumes:
25
+      - .:/app
26
+    working_dir: /app
27
+    ports:
28
+      - "8000:8000"
29
+    depends_on:
30
+      - db
31
+    environment:
32
+      - DEBUG=1
33
+      - DB_NAME=commudb
34
+      - DB_USER=commuuser
35
+      - DB_PASSWORD=commupass
36
+      - DB_HOST=db
37
+      - DB_PORT=5432
38
+
39
+volumes:
40
+  postgres_data:

+ 38 - 0
main.py

@@ -0,0 +1,38 @@
1
+from commu.infrastructure.memory_user_repository import InMemoryUserRepository
2
+from commu.infrastructure.memory_post_repository import InMemoryPostRepository
3
+from commu.usecases.user_service import UserService
4
+from commu.usecases.post_service import PostService
5
+from commu.entities.user import UserRole
6
+
7
+def main():
8
+    user_repo = InMemoryUserRepository()
9
+    post_repo = InMemoryPostRepository()
10
+    user_service = UserService(user_repo)
11
+    post_service = PostService(post_repo, user_repo)
12
+
13
+    # Create users
14
+    admin = user_service.register("alice", UserRole.ADMIN)
15
+    user = user_service.register("bob", UserRole.USER)
16
+    guest = user_service.register("guest", UserRole.GUEST)
17
+
18
+    # User creates post
19
+    post = post_service.create_post(user.id, "Hello world!", images=["img1.png"], videos=["vid1.mp4"])
20
+    print(f"Created Post: {post.text}, by {user.username}")
21
+
22
+    # Post search
23
+    found = post_service.search_posts("hello")
24
+    print(f"Found {len(found)} posts containing 'hello'.")
25
+
26
+    # Post rating
27
+    post_service.rate_post(post.id, admin.id, 5)
28
+    post_service.rate_post(post.id, user.id, 4)
29
+    print(f"Average Rating: {post.average_rating()}")
30
+
31
+    # Guest tries to create post (should fail)
32
+    try:
33
+        post_service.create_post(guest.id, "I am a guest")
34
+    except PermissionError as e:
35
+        print("Guest cannot create post:", e)
36
+
37
+if __name__ == "__main__":
38
+    main()

+ 22 - 0
manage.py

@@ -0,0 +1,22 @@
1
+#!/usr/bin/env python
2
+"""Django's command-line utility for administrative tasks."""
3
+import os
4
+import sys
5
+
6
+
7
+def main():
8
+    """Run administrative tasks."""
9
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_commu.settings')
10
+    try:
11
+        from django.core.management import execute_from_command_line
12
+    except ImportError as exc:
13
+        raise ImportError(
14
+            "Couldn't import Django. Are you sure it's installed and "
15
+            "available on your PYTHONPATH environment variable? Did you "
16
+            "forget to activate a virtual environment?"
17
+        ) from exc
18
+    execute_from_command_line(sys.argv)
19
+
20
+
21
+if __name__ == '__main__':
22
+    main()

+ 3 - 0
requirements.txt

@@ -0,0 +1,3 @@
1
+pytest
2
+Django>=4.2
3
+psycopg2-binary

tum/whitesports - Gogs: Simplico Git Service

Sin descripción

handler.php 45KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  1. <?php
  2. /**
  3. * Copy & Delete Posts – Post requests handler file.
  4. *
  5. * @package CDP
  6. * @subpackage PostHandler
  7. * @author CopyDeletePosts
  8. * @since 1.0.0
  9. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  10. /** –– **\
  11. * Main handler + It will also sanitize and verify that request a little bit.
  12. * @since 1.0.0
  13. */
  14. add_action('wp_ajax_cdp_action_handling', function () {
  15. if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
  16. if (isset($_POST['token']) && $_POST['token'] == 'cdp' && isset($_POST['f']) && is_admin()) {
  17. // Expand execution time
  18. if (intval(ini_get('max_execution_time')) < 7200)
  19. set_time_limit(0);
  20. // Get WP-Plugin path
  21. $premium_plugin = 'copy-delete-posts-premium/copy-delete-posts-premium.php';
  22. $premium_dir = WP_PLUGIN_DIR . '/' . 'copy-delete-posts-premium';
  23. $pplugin_path = $premium_dir . '/handler/premium.php';
  24. // Load premium content if the plugin is here
  25. if (is_dir($premium_dir) && is_plugin_active($premium_plugin))
  26. require_once($pplugin_path);
  27. // Is premium function
  28. $areWePro = is_plugin_active($premium_plugin);
  29. // Get user roles and check if the role is permmited to use plugin
  30. $access = false;
  31. $current_user = wp_get_current_user();
  32. $access_roles = get_option('_cdp_globals');
  33. if (!isset($access_roles['roles']))
  34. $access_roles = array();
  35. foreach ($current_user->roles as $role => $name)
  36. if ($name == 'administrator' || (isset($access_roles['roles'][$name]) && $access_roles['roles'][$name] == 'true')) {
  37. $access = true;
  38. break;
  39. }
  40. // Check user permission
  41. if ($access === true) {
  42. // Pointers
  43. if ($_POST['f'] == 'no_intro')
  44. cdp_add_new_no_intro();
  45. else if ($_POST['f'] == 'intro_again')
  46. cdp_add_new_intro();
  47. else if ($_POST['f'] == 'save_options')
  48. cdp_save_plugin_options($areWePro);
  49. else if ($_POST['f'] == 'copy_post')
  50. cdp_insert_new_post($areWePro);
  51. else if ($_POST['f'] == 'get_settings')
  52. cdp_get_profile();
  53. else if ($_POST['f'] == 'get_all_settings')
  54. cdp_get_all_profiles();
  55. else if ($_POST['f'] == 'save_profiles' && $areWePro)
  56. cdp_save_profile_set();
  57. else if ($_POST['f'] == 'get_all_posts')
  58. cdp_get_all_posts();
  59. else if ($_POST['f'] == 'delete_them')
  60. cdp_delete_posts();
  61. else if ($_POST['f'] == 'get_formatted_time' && $areWePro)
  62. cdp_formatted_time();
  63. else if ($_POST['f'] == 'set_default_profile')
  64. cdp_set_default_profile();
  65. else if ($_POST['f'] == 'get_default_profile')
  66. cdp_get_default_profile();
  67. else if ($_POST['f'] == 'clear_crons')
  68. cdp_clear_all_crons();
  69. else if ($_POST['f'] == 'i_saw_this_noti')
  70. cdp_set_noti_as_seen();
  71. else if ($_POST['f'] == 'try_to_hide_the_tasks')
  72. cdp_just_hide_task();
  73. else if ($_POST['f'] == 'try_to_kill_the_tasks')
  74. cdp_just_kill_task();
  75. else if ($_POST['f'] == 'give_me_current_tasks')
  76. cdp_just_get_tasks();
  77. else if ($_POST['f'] == 'hide_cron_notice')
  78. cdp_hide_perf_notice();
  79. else if ($_POST['f'] == 'review_dismiss')
  80. cdp_review();
  81. else if ($_POST['f'] == 'debug_function')
  82. cdp_debug_function();
  83. else if ($_POST['f'] == 'delete_success_img')
  84. delete_option('_cdp_show_copy');
  85. else if ($_POST['f'] == 'save_redi_state' && $areWePro)
  86. cdpp_save_redi_state();
  87. else if ($_POST['f'] == 'multi_redi_importer' && $areWePro)
  88. cdpp_redis_importer();
  89. else if ($_POST['f'] == 'save_redirections' && $areWePro)
  90. cdpp_save_redirections();
  91. else if ($_POST['f'] == 'delete_redirect' && $areWePro)
  92. cdpp_delete_redirection();
  93. else if ($_POST['f'] == 'switch_redirects' && $areWePro)
  94. cdpp_switch_redirects();
  95. else if ($_POST['f'] == 'get_authors' && $areWePro)
  96. cdpp_get_authors();
  97. else if ($_POST['f'] == 'get_curr_time' && $areWePro)
  98. cdpp_get_curr_s_time();
  99. else if ($_POST['f'] == 'get_post_export' && $areWePro)
  100. cdpp_get_for_export();
  101. else if ($_POST['f'] == 'import_posts' && $areWePro)
  102. cdpp_take_for_import();
  103. else if ($_POST['f'] == 'save_aci' && $areWePro)
  104. cdpp_save_cleanup_settings();
  105. else if ($_POST['f'] == 'get_aci' && $areWePro)
  106. cdpp_get_cleanup_settings();
  107. else if ($_POST['f'] == 'turn_off_aci' && $areWePro)
  108. cdpp_turn_the_acii_off();
  109. else if ($_POST['f'] == 'i_love_squirrels' && $areWePro)
  110. cdpp_squirrel();
  111. else
  112. echo 'error';
  113. } else
  114. echo 'error';
  115. } else
  116. echo 'no_access';
  117. } else
  118. echo 'no_access';
  119. wp_die();
  120. });
  121. /** –– * */
  122. /** –– **\
  123. * This function will be fired when user don't want to see intro – never again.
  124. * @since 1.0.6
  125. */
  126. function cdp_review() {
  127. // Option
  128. $method = sanitize_text_field($_POST['decision']);
  129. // Get user id and array from db
  130. $user_id = get_current_user_id();
  131. $already = get_option('_cdp_review', false);
  132. // Create if not exists
  133. if ($already == false)
  134. $already = array('installed' => time(), 'users' => array());
  135. // Set dismiss
  136. $already['users'][$user_id] = array();
  137. $already['users'][$user_id]['dismiss'] = (($method == 'remind') ? time() : true);
  138. // Add option to datbase if not exit.
  139. $opt = update_option('_cdp_review', $already);
  140. // Return success
  141. echo json_encode(array('status' => 'success'));
  142. }
  143. /** –– * */
  144. /** –– **\
  145. * This function will be fired when user don't want to see intro – never again.
  146. * @since 1.0.0
  147. */
  148. function cdp_add_new_no_intro() {
  149. // Get user id and array from db
  150. $user_id = get_current_user_id();
  151. $already = get_option('_cdp_no_intro');
  152. // Check if it's first time that user checked this option.
  153. if (!$already)
  154. $already = array($user_id);
  155. // If it already exists just add another user.
  156. else if (!in_array($user_id, $already))
  157. array_push($already, $user_id);
  158. // If the user already exists exit.
  159. else
  160. exit;
  161. // Add option to datbase if not exit.
  162. $opt = update_option('_cdp_no_intro', $already);
  163. }
  164. /** –– * */
  165. /** –– **\
  166. * This function will be fired when user want to see intro – again.
  167. * @since 1.0.0
  168. */
  169. function cdp_add_new_intro() {
  170. // Get user id and array from db
  171. $user_id = get_current_user_id();
  172. $already = get_option('_cdp_no_intro');
  173. // Check if it's first time that user checked this option.
  174. if ($already && in_array($user_id, $already))
  175. unset($already[array_search($user_id, $already, true)]);
  176. // If the user no exists exit.
  177. else
  178. exit;
  179. // Add option to database if not exit.
  180. $opt = update_option('_cdp_no_intro', $already);
  181. }
  182. /** –– * */
  183. /** –– **\
  184. * This function will be fired when user want to save plugin options – again.
  185. * @since 1.0.0
  186. */
  187. function cdp_save_plugin_options($areWePro) {
  188. // Get the info about our professionalness
  189. $areWePro = $areWePro;
  190. // Get new options and current profile.
  191. $options = ((isset($_POST['options'])) ? cdp_sanitize_array($_POST['options']) : false);
  192. $entire = ((isset($_POST['entire'])) ? cdp_sanitize_array($_POST['entire']) : false);
  193. $profile = ((isset($_POST['profile'])) ? sanitize_text_field($_POST['profile']) : false);
  194. // Get current options and profiles.
  195. $a_or = get_option('_cdp_profiles');
  196. $already = get_option('_cdp_profiles');
  197. $g_or = get_option('_cdp_globals');
  198. $globals = get_option('_cdp_globals');
  199. // Check if it's first time – create array.
  200. if (!is_array($already))
  201. $already = array();
  202. if (!is_array($globals))
  203. $globals = array();
  204. // Add display name for this profile
  205. $profile = preg_replace('/\s+/', '_', trim(strtolower($profile)));
  206. // Write new settings for this profile.
  207. $already[$profile] = $options;
  208. $already[$profile]['usmplugin'] = 'false';
  209. $already[$profile]['yoast'] = 'false';
  210. $already[$profile]['woo'] = 'false';
  211. if ($areWePro)
  212. $already[$profile] = cdpp_filter_premium_opts($already, $options, $profile);
  213. if (!isset($already[$profile]['names']['display']) || (strlen(trim($already[$profile]['names']['display'])) <= 0))
  214. $already[$profile]['names']['display'] = $profile;
  215. $globals = $entire;
  216. // Check if there is default profile
  217. if (!array_key_exists('default', $already) || !array_key_exists('title', $already['default'])) {
  218. $already['default'] = array();
  219. if (function_exists('cdp_default_options'))
  220. $already['default'] = cdp_default_options();
  221. if (function_exists('cdp_default_global_options'))
  222. $globals['others'] = cdp_default_global_options();
  223. }
  224. // Add new options to database.
  225. $s1 = update_option('_cdp_globals', $globals);
  226. $s2 = update_option('_cdp_profiles', $already);
  227. // Check if success while uploading
  228. if (($s1 || $s2) || ($globals == $g_or) || ($already == $a_or))
  229. echo 'success';
  230. else
  231. echo 'error';
  232. }
  233. /** –– * */
  234. /** –– **\
  235. * This function will be fired when user want to save plugin options – again.
  236. * @since 1.0.0
  237. */
  238. function cdp_insert_new_post($areWePro = false) {
  239. // Performance copy time start
  240. $timein = microtime(true);
  241. // Create output array which will be returned to requester
  242. $output = array('status' => 'success');
  243. // Get ID(s) of post(s)
  244. $ids = ((isset($_POST['id'])) ? cdp_sanitize_array($_POST['id']) : false);
  245. // Get all important pieces of information from requester
  246. $data = ((isset($_POST['data'])) ? cdp_sanitize_array($_POST['data']) : false);
  247. $site = isset($_POST['data']['site']) ? sanitize_text_field($_POST['data']['site']) : false;
  248. $times = isset($_POST['data']['times']) ? sanitize_text_field($_POST['data']['times']) : 1;
  249. $swap = isset($_POST['data']['swap']) ? sanitize_text_field($_POST['data']['swap']) : false;
  250. $profile = isset($_POST['data']['profile']) ? sanitize_text_field($_POST['data']['profile']) : 'default';
  251. $origin = isset($_POST['origin']) ? sanitize_text_field($_POST['origin']) : false;
  252. $custom = isset($_POST['data']['custom']) ? cdp_sanitize_array($_POST['data']['custom']) : false;
  253. // Load default options for selected profile
  254. $defaults = get_option('_cdp_profiles')[$profile];
  255. // Settings for this copy
  256. $settings = (($data['type'] != 'copy-quick' && $custom != false) ? $custom : $defaults);
  257. if (!isset($settings['names']))
  258. $settings['names'] = $defaults['names'];
  259. // Convert string to boolean – only for much less code later
  260. foreach ($settings as $setting => $val)
  261. if ($setting != 'names')
  262. $settings[$setting] = (($val == 'true') ? true : false);
  263. /**
  264. * This local function filters post data by user settings
  265. * @param $post (array of wordpress post/page data)
  266. * @param $settings (array of preselected settings of profile or by user)
  267. * @return array with insert ready values for wordpress post || false on wrong $post
  268. */
  269. function cdp_filter_post($post, $swap, $opt, $settings, $taxonomies = false, $areWePro) {
  270. // If $post has wrong format return false
  271. if (!(is_array($post) || is_object($post)))
  272. return false;
  273. // Array for formatted and prepared taxonomy
  274. $ft = array();
  275. $buin = array('link_category', 'nav_menu', 'post_tag', 'category', 'post_format');
  276. // Loop thorugh all taxonomies from post
  277. foreach ($taxonomies as $taxonomy) {
  278. // Set the name to shorted variable
  279. $tn = $taxonomy->taxonomy;
  280. // Check if it's private taxonomy and if it's set in options
  281. if ($tn == 'link_category' && !$settings['link_category'])
  282. continue;
  283. if ($tn == 'nav_menu' && !$settings['nav_menu'])
  284. continue;
  285. if ($tn == 'post_tag' && !$settings['post_tag'])
  286. continue;
  287. if ($tn == 'category' && !$settings['category'])
  288. continue;
  289. if ($tn == 'post_format' && !$settings['format'])
  290. continue;
  291. // Don't copy custom taxonomy if it's not checked
  292. if (!in_array($tn, $buin) && !$settings['taxonomy'])
  293. continue;
  294. // Push next term of existing taxonomy
  295. if (isset($ft[$tn]))
  296. array_push($ft[$tn], $taxonomy->term_id);
  297. // Create new taxonomy and push new term
  298. else {
  299. $ft[$tn] = array();
  300. array_push($ft[$tn], $taxonomy->term_id);
  301. }
  302. }
  303. // Create array with required values and contant values
  304. $new = array(
  305. 'post_title' => ($settings['title'] ? cdp_create_title($post['post_title'], $settings['names'], $post['ID'], $areWePro) : __('Untitled Copy', 'copy-delete-posts')),
  306. 'post_date' => ($settings['date'] ? $post['post_date'] : current_time('mysql')),
  307. 'post_status' => ($settings['status'] ? $post['post_status'] : 'draft'),
  308. 'post_author' => ($settings['author'] ? $post['post_author'] : wp_get_current_user()->ID),
  309. 'post_content' => ($settings['content']) ? $post['post_content'] : ' ',
  310. 'comment_status' => $post['comment_status'], // that's additional element which cannot be edited by user
  311. 'post_parent' => $post['post_parent'] // that's additional element which cannot be edited by user
  312. );
  313. // Converter
  314. if ((($opt == '2' && $swap == 'true') || $swap == 'true') && $areWePro && function_exists('cdpp_post_converter'))
  315. $new['post_type'] = cdpp_post_converter($post['post_type']);
  316. else
  317. $new['post_type'] = $post['post_type'];
  318. // Add optional values of post – depending on settings
  319. if ($settings['slug'])
  320. $new['post_name'] = $post['post_name'];
  321. if ($settings['excerpt'])
  322. $new['post_excerpt'] = $post['post_excerpt'];
  323. if ($settings['template'])
  324. $new['page_template'] = $post['page_template'];
  325. if ($settings['password'])
  326. $new['post_password'] = $post['post_password'];
  327. if ($settings['menu_order'])
  328. $new['menu_order'] = $post['menu_order'];
  329. if ($settings['category'])
  330. $new['post_category'] = $post['post_category'];
  331. if ($settings['post_tag'])
  332. $new['tags_input'] = $post['tags_input'];
  333. if ($taxonomies != false)
  334. $new['tax_input'] = $ft;
  335. // Return filtered data of current post
  336. return $new;
  337. }
  338. /**
  339. * This local function filters post data by user settings
  340. * @param $metas (array of wordpress post/page meta data)
  341. * @param $settings (array of preselected settings of profile or by user)
  342. * @return array with metadata values for post || false on wrong $metas
  343. */
  344. function cdp_filter_meta($metas, $settings, $id, $areWePro, $site, $title) {
  345. // If $metas has wrong format return false
  346. if (!(is_array($metas) || is_object($metas)))
  347. return false;
  348. // Create empty array for filtered meta data
  349. $prepared = array(
  350. // Add or replace ours copy tracker
  351. array('_cdp_origin' => $id),
  352. array('_cdp_origin_site' => $site),
  353. array('_cdp_origin_title' => $title),
  354. array('_cdp_counter' => '0')
  355. );
  356. // Iterate through every meta index
  357. foreach ($metas as $meta => $vals) {
  358. // Conditions
  359. $a = ($areWePro && function_exists('cdpp_check_yoast')) ? cdpp_check_yoast($settings, $meta) : false;
  360. $b = ($areWePro && function_exists('cdpp_check_usm')) ? cdpp_check_usm($settings, $meta) : false;
  361. $c = ($areWePro && function_exists('cdpp_check_woo')) ? cdpp_check_woo($settings, $meta, $id) : false;
  362. $d = ($settings['f_image'] && $meta == '_thumbnail_id') ? true : false;
  363. $e = (mb_substr($meta, 0, 4) == '_wp_') ? true : false;
  364. $f = ($meta == '_thumbnail_id' && $settings['f_image']) ? true : false;
  365. $g = ($meta == '_cdp_origin') ? true : false;
  366. $h = (mb_substr($meta, 0, 11) == '_elementor_') ? true : false;
  367. // $i = (isset($settings['all_metadata']) && $settings['all_metadata'] == 'true') ? true : false;
  368. // If any of above condition is true pass the meta tag
  369. if ($a || $b || $c || $d || $e || $f || $g || $h /*|| $i*/) {
  370. // Prepare data and insert filtered to results
  371. foreach ($vals as $val)
  372. array_push($prepared, array($meta => $val));
  373. } else {
  374. // error_log(print_r($vals, true));
  375. }
  376. }
  377. // Return results
  378. return $prepared;
  379. }
  380. /**
  381. * This local function format title by user settings
  382. * @param $title (string)
  383. * @param $settings (array of name settings preselected in profile)
  384. * @return string formated title
  385. */
  386. function cdp_create_title($title, $settings, $id, $areWePro) {
  387. // Date formats
  388. $date_format = intval($settings['format']);
  389. // Get right format
  390. if ($date_format == 1)
  391. $date_format = 'm/d/Y';
  392. else if ($date_format == 2)
  393. $date_format = 'd/m/Y';
  394. else {
  395. if ($areWePro && function_exists('cdpp_custom_date'))
  396. $date_format = cdpp_custom_date($settings);
  397. else
  398. $date_format = 'd/m/Y';
  399. }
  400. // Create date and time replacements
  401. $curr = current_time('timestamp', true);
  402. $date = date($date_format, $curr);
  403. $time = date('H:i:s', $curr);
  404. // Concat whole title with prefix and suffix
  405. $new_title = $settings['prefix'] . ' ' . $title . ' ' . $settings['suffix'];
  406. // Make replace of placeholders
  407. $new_title = str_replace('[CurrentDate]', $date, $new_title);
  408. $new_title = str_replace('[CurrentTime]', $time, $new_title);
  409. // Return formatted title
  410. return $new_title;
  411. }
  412. /**
  413. * This local function inserts whole post into database
  414. * @param $data (array prepared by cdp_filter_post function)
  415. * @param $times (int how many times should this function copy post)
  416. * @return array of new inserted post(s) and error status
  417. * Structure of return array: { ids: [$ids], error: (count of errors) }
  418. */
  419. function cdp_insert_post($id, $data, $times, $areWePro, $isChild = false, $p_ids = null, $site) {
  420. // Get Wordpress database
  421. global $wpdb;
  422. // Create empty array for new id(s) and error(s)
  423. $results = array('ids' => array(), 'error' => 0, 'counter' => 0);
  424. // Prevent SQL injection
  425. if (!is_numeric($id)) {
  426. echo json_encode(array('status' => 'error', 'message' => __('Invalid ID argument.', 'copy-delete-posts')));
  427. exit;
  428. }
  429. // And just in case
  430. $id = esc_sql(intval($id));
  431. // Get Counter value
  432. $prefix = (($site != -1) ? $wpdb->get_blog_prefix($site) : $wpdb->get_blog_prefix());
  433. $newestId = $wpdb->get_results("SELECT post_id FROM {$prefix}postmeta WHERE meta_key = '_cdp_origin' AND meta_value = {$id} ORDER BY post_id DESC LIMIT 1", ARRAY_A);
  434. $newestId = ((array_key_exists(0, $newestId)) ? (intval($newestId[0]['post_id'])) : false);
  435. if (isset($newestId) && $newestId != false && $newestId > 0)
  436. $counter = $wpdb->get_results("SELECT meta_value AS 'Counter' FROM {$prefix}postmeta WHERE meta_key = '_cdp_counter' AND post_id = {$newestId} ORDER BY post_id DESC", ARRAY_A)[0]['Counter'];
  437. else
  438. $counter = 1;
  439. $base_title = $data['post_title'];
  440. $counter = intval($counter) + 1;
  441. // Handle multisite for premium
  442. if ($areWePro && function_exists('cdpp_handle_multisite'))
  443. cdpp_handle_multisite($site);
  444. // Loop for each post iteration
  445. for ($i = 0; $i < $times; ++$i) {
  446. // Change parent if it's child
  447. if ($isChild)
  448. $data['post_parent'] = $p_ids['posts'][$i];
  449. // Replace title with Counter if multiple copies
  450. $data['post_title'] = str_replace('[Counter]', ($counter + $i), $base_title);
  451. // Insert post with filtered data
  452. $new = wp_insert_post($data, true);
  453. // Check if the post is inserted successfully and append array
  454. if (is_numeric($new))
  455. array_push($results['ids'], $new);
  456. else
  457. $results['error'] ++;
  458. }
  459. // Handle multisite for premium fix
  460. if ($areWePro && function_exists('cdpp_handle_multisite_after'))
  461. cdpp_handle_multisite_after($site);
  462. // Set first counter number for future
  463. $results['counter'] = $counter;
  464. // Return array with results
  465. return $results;
  466. }
  467. /**
  468. * This local function filter and adds missing meta to added post
  469. * @param $ids (array of post ids)
  470. * @param $metas (filtered meta data with cdp_filter_meta function)
  471. * @return array structure below
  472. * { ids: { [id] => [failed times]}, error: { [id] => [failed times]} }
  473. */
  474. function cdp_insert_post_meta($ids, $metas, $areWePro, $counter, $site) {
  475. // Handle multisite for premium
  476. if ($areWePro && function_exists('cdpp_handle_multisite'))
  477. cdpp_handle_multisite($site);
  478. // Create empty array for new id(s) and error(s)
  479. $results = array('ids' => array(), 'error' => array());
  480. // Iterate through every inserted post
  481. foreach ($ids as $id) {
  482. // Iterate through every meta tag
  483. foreach ($metas as $meta_id => $meta) {
  484. // Get individual data from metas array
  485. foreach ($meta as $key => $val) {
  486. // Replace the counter with dynamic value
  487. if ($key == '_cdp_counter')
  488. $val = $counter;
  489. // Insert meta tag
  490. $res = add_post_meta($id, $key, $val);
  491. // Check if the insert was successfull
  492. if ($res != false) {
  493. if (!isset($results['ids'][$id]))
  494. $results['ids'][$id] = [];
  495. array_push($results['ids'][$id], array($key, $val));
  496. } else {
  497. if (!isset($results['error'][$id]))
  498. $results['error'][$id] = [];
  499. array_push($results['error'][$id], array($key, $val));
  500. }
  501. }
  502. }
  503. // Iterate the counter
  504. $counter++;
  505. }
  506. // Fix multisite handler
  507. if ($areWePro && function_exists('cdpp_handle_multisite_after'))
  508. cdpp_handle_multisite_after($site);
  509. // Return the results
  510. return $results;
  511. }
  512. /**
  513. * This local function search for childs and catch their IDs
  514. * @param $id string/int (post id)
  515. * @return array of child(s) ID(s)
  516. */
  517. function cdp_check_childs($id) {
  518. $childs = [];
  519. $childrens = get_children(array('post_parent' => $id));
  520. foreach ($childrens as $i => $child)
  521. array_push($childs, $child->ID);
  522. return $childs;
  523. }
  524. /**
  525. * This local function copies original attachments
  526. * @param $path string (path to original file)
  527. * @return string path to new file
  528. */
  529. function cdp_copy_attachment($path = '', $destination) {
  530. if ($path == '')
  531. return false;
  532. $dirname = $destination;
  533. $name = basename($path);
  534. $actual_name = pathinfo($name, PATHINFO_FILENAME);
  535. $original_name = $actual_name;
  536. $extension = pathinfo($name, PATHINFO_EXTENSION);
  537. $i = 1;
  538. while (file_exists($dirname . '/' . $actual_name . "." . $extension)) {
  539. $actual_name = (string) $original_name . '-' . $i;
  540. $name = $actual_name . '.' . $extension;
  541. $i++;
  542. }
  543. copy($path, $dirname . '/' . $name);
  544. return $dirname . '/' . $name;
  545. }
  546. /**
  547. * This local function gets copy and insert attachments
  548. * @param $id int/string of post
  549. * @return array of inserted attachments
  550. */
  551. function cdp_insert_attachments($id, $inserted_posts, $areWePro, $site) {
  552. $inserts = array();
  553. $media = get_attached_media('', $id);
  554. // Handle multisite for premium
  555. if ($areWePro && function_exists('cdpp_handle_multisite'))
  556. cdpp_handle_multisite($site);
  557. // Fix wordpress multisite path
  558. add_filter('upload_dir', 'cdp_fix_upload_paths');
  559. $wp_upload_dir = wp_upload_dir();
  560. remove_filter('upload_dir', 'cdp_fix_upload_paths');
  561. // Handle multisite for premium fix
  562. if ($areWePro && function_exists('cdpp_handle_multisite_after'))
  563. cdpp_handle_multisite_after($site);
  564. foreach ($media as $i => $m) {
  565. if (get_attached_file($m->ID) == '')
  566. continue;
  567. $path = cdp_copy_attachment(get_attached_file($m->ID), $wp_upload_dir['path']);
  568. $filename = $path;
  569. $parent_post_id = $inserted_posts['ids'][0];
  570. $filetype = wp_check_filetype(basename($filename), null);
  571. // Handle multisite for premium
  572. if ($areWePro && function_exists('cdpp_handle_multisite'))
  573. cdpp_handle_multisite($site);
  574. $attachment = array(
  575. 'guid' => $wp_upload_dir['url'] . '/' . basename($filename),
  576. 'post_mime_type' => $filetype['type'],
  577. 'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
  578. 'post_content' => '',
  579. 'post_status' => 'inherit'
  580. );
  581. $attach_id = wp_insert_attachment($attachment, $filename, $parent_post_id);
  582. array_push($inserts, array('url' => wp_get_attachment_url($attach_id), 'id' => $attach_id));
  583. $attach_data = wp_generate_attachment_metadata($attach_id, $filename);
  584. wp_update_attachment_metadata($attach_id, $attach_data);
  585. // Handle multisite for premium fix
  586. if ($areWePro && function_exists('cdpp_handle_multisite_after'))
  587. cdpp_handle_multisite_after($site);
  588. }
  589. return $inserts;
  590. }
  591. /**
  592. * This local function gets comments and copy them
  593. * @param $id int/string of base post
  594. * @param $dests array of post ids where the comms from $id should be copied
  595. * @return array of inserted comments
  596. */
  597. function cdp_copy_comments($id, $dests) {
  598. $comments = get_comments(array('post_id' => $id));
  599. $curr = current_time('mysql');
  600. $all_inserts = array();
  601. $all_inserts['fix_try'] = array();
  602. $all_inserts['olds'] = '';
  603. foreach ($dests as $dest) {
  604. $p = 0;
  605. $olds = array();
  606. $cm1 = $comments;
  607. foreach ($cm1 as $i => $c) {
  608. $c = $c->to_array();
  609. $old_id = $c['comment_ID'];
  610. $parent = $c['comment_parent'];
  611. $c['comment_date'] = $curr;
  612. $c['comment_date_gmt'] = $curr;
  613. $c['comment_post_ID'] = $dest;
  614. $c['comment_parent'] = 0;
  615. if ($parent != "0")
  616. $p++;
  617. $new_id = @wp_insert_comment($c);
  618. $olds[$old_id] = array('new' => $new_id, 'old_parent_id' => $parent);
  619. array_push($all_inserts, array('old' => $old_id, 'new' => $new_id, 'parent' => $parent));
  620. }
  621. if ($p != 0) {
  622. $cm2 = $comments;
  623. foreach ($cm2 as $j => $m) {
  624. if ($m->comment_parent != "0" && $olds[$m->comment_ID]['old_parent_id'] == $m->comment_parent) {
  625. $post = get_comment($olds[$m->comment_ID]['new']);
  626. $post = $post->to_array();
  627. $post['comment_parent'] = $olds[$m->comment_parent]['new'];
  628. wp_update_comment($post);
  629. }
  630. }
  631. }
  632. }
  633. return $all_inserts;
  634. }
  635. // Main code for this duplication – for each id (post) do whole process
  636. function cdp_process_ids($ids, $swap, $settings, $times, $site, $areWePro, $g, $isChild = false, $p_ids = null) {
  637. // Make it clear
  638. $globals = cdp_default_global_options();
  639. if ($g != false)
  640. $globals = $g;
  641. $g = $globals['others'];
  642. // Return data storage
  643. $output = [];
  644. $new_posts = array('parents' => array(), 'childs' => array(), 'ids' => array());
  645. // Iterate each id
  646. foreach ($ids as $id) {
  647. // Get post data and meta data
  648. $post = get_post($id)->to_array();
  649. $meta = get_post_custom($id);
  650. $taxonomies = wp_get_object_terms($id, get_taxonomies());
  651. // Check if this post type is allowed to copy
  652. $type = $post['post_type'];
  653. if ($g['cdp-content-pages'] == 'false' && $type == 'page')
  654. continue;
  655. if ($g['cdp-content-posts'] == 'false' && $type == 'post')
  656. continue;
  657. if ($g['cdp-content-custom'] == 'false' && ($type != 'page' && $type != 'post'))
  658. continue;
  659. // Post converting?
  660. $pConv = false;
  661. if (array_key_exists('postConverter', $globals))
  662. $pConv = $globals['postConverter'];
  663. // Run process and validate response
  664. $childrens = cdp_check_childs($id); // if sizeof($this) == has childs
  665. $post_data = cdp_filter_post($post, $swap, $pConv, $settings, $taxonomies, $areWePro, $swap); // can be false
  666. $meta_data = cdp_filter_meta($meta, $settings, $id, $areWePro, $site, $post_data['post_title']); // can be false
  667. $inserted_posts = cdp_insert_post($id, $post_data, $times, $areWePro, $isChild, $p_ids, $site); // $res['error'] must be == 0
  668. $inserted_metas = cdp_insert_post_meta($inserted_posts['ids'], $meta_data, $areWePro, $inserted_posts['counter'], $site); // sizeof($res['error']) must be == 0
  669. // Comments copy
  670. if ($settings['comments'])
  671. $inserted_comments = cdp_copy_comments($id, $inserted_posts['ids']);
  672. $cms = get_comments(array('post_id' => $id));
  673. // Post format
  674. if ($settings['format'])
  675. foreach ($inserted_posts['ids'] as $i => $tid)
  676. $isReFormat = set_post_format($tid, get_post_format($id));
  677. // Featured image copy
  678. if ($settings['attachments'])
  679. $inserted_attachments = cdp_insert_attachments($id, $inserted_posts, $areWePro, $site);
  680. else
  681. $inserted_attachments = false;
  682. // Copy childrens recursively if exist
  683. if ($settings['children'] && sizeof($childrens) > 0) {
  684. $child_helpers = array('posts' => $inserted_posts['ids']);
  685. $inserted_childs = cdp_process_ids($childrens, $swap, $settings, $times, $site, $areWePro, $globals, true, $child_helpers);
  686. array_push($new_posts['childs'], array($id => $inserted_childs['$new_posts']['ids']));
  687. }
  688. // Add new inserted IDs
  689. foreach ($inserted_posts['ids'] as $i_id)
  690. array_push($new_posts['parents'], $i_id);
  691. // Merge for easier read
  692. $new_posts['ids'] = array_merge($new_posts['ids'], $new_posts['parents'], $new_posts['childs']);
  693. }
  694. // Return all data to main request
  695. return array('$output' => $output, '$new_posts' => $new_posts);
  696. }
  697. // Run the machine for selected post(s)
  698. $g = get_option('_cdp_globals', false);
  699. $new_insertions = cdp_process_ids($ids, $swap, $settings, $times, $site, $areWePro, $g);
  700. // Handle multisite for premium
  701. if ($areWePro && function_exists('cdpp_handle_multisite'))
  702. cdpp_handle_multisite($site);
  703. $pConv = false;
  704. if (array_key_exists('postConverter', $g) && $areWePro)
  705. $pConv = (($g['postConverter'] === '2' || $g['postConverter'] === 2) ? true : false);
  706. // Output link if it's edited post
  707. $aCop = ((array_key_exists('afterCopy', $g)) ? $g['afterCopy'] : '1');
  708. if (($data['type'] == 'copy-custom-link' || $aCop == '2'))
  709. $output['link'] = get_edit_post_link($new_insertions['$new_posts']['parents'][0], 'x');
  710. if ($pConv == true && !($data['type'] == 'copy-custom-link' || $aCop == '2'))
  711. $output['link'] = 'pConv';
  712. else
  713. update_option('_cdp_show_copy', true);
  714. // Handle multisite for premium fix
  715. if ($areWePro && function_exists('cdpp_handle_multisite_after'))
  716. cdpp_handle_multisite_after($site);
  717. // Check performance by time
  718. $copyTime = microtime(true) - $timein;
  719. $copyTimePerOne = $copyTime / $times;
  720. // Set only if had good performance all the time
  721. $isSlowPerf = true;
  722. if (get_option('cdp_latest_slow_performance', false) == false) {
  723. $isSlowPerf = false;
  724. }
  725. // Check if the copy time of one page was slower than 0.051 of second
  726. if ($copyTimePerOne > 0.051) {
  727. $isSlowPerf = true;
  728. }
  729. // Set the performance status
  730. update_option('cdp_latest_slow_performance', $isSlowPerf);
  731. // Update history with logs
  732. $logs = get_option('cdp_copy_logs_times', array());
  733. if (sizeof($logs) >= 50) {
  734. $logs = array_slice($logs, 0, 48);
  735. }
  736. $logs = array_values($logs);
  737. array_unshift($logs, array('amount' => $times, 'time' => $copyTime, 'perOne' => $copyTimePerOne, 'data' => time(), 'memory' => memory_get_usage(), 'peak' => memory_get_peak_usage(true)));
  738. update_option('cdp_copy_logs_times', $logs);
  739. echo json_encode(cdp_sanitize_array($output));
  740. }
  741. /** –– * */
  742. /** –– **\
  743. * This function will return profile information for presets.
  744. * @return object of settings by requested profile
  745. * @since 1.0.0
  746. */
  747. function cdp_get_profile() {
  748. if (function_exists('cdpp_get_all_profiles'))
  749. cdpp_get_profile();
  750. else {
  751. // Search for the settings of profile
  752. $settings = get_option('_cdp_profiles')['default'];
  753. // Display those settings
  754. echo json_encode(cdp_sanitize_array($settings));
  755. }
  756. }
  757. /** –– * */
  758. /** –– **\
  759. * This function will return all profile information for manager.
  760. * @return object of settings by requested profile
  761. * @since 1.0.0
  762. */
  763. function cdp_get_all_profiles() {
  764. if (function_exists('cdpp_get_all_profiles'))
  765. cdpp_get_all_profiles();
  766. else
  767. cdp_get_profile();
  768. }
  769. /** –– * */
  770. /** –– **\
  771. * This function will return all not trashed posts
  772. * @return object of posts and success or fail message
  773. */
  774. function cdp_get_all_posts() {
  775. $output = array();
  776. $args = array(
  777. 'numberposts' => -1,
  778. 'post_type' => 'post',
  779. 'post_status' => 'publish,private,draft,future,pending,inherit,sticky'
  780. );
  781. $output['posts'] = get_posts($args);
  782. $args['post_type'] = 'page';
  783. $output['pages'] = get_posts($args);
  784. $output['custom'] = array();
  785. $post_types = get_post_types(array('public' => true, '_builtin' => false));
  786. if (sizeof($post_types) > 0)
  787. $output['custom'] = get_posts(array(
  788. 'post_type' => $post_types,
  789. 'numberposts' => -1,
  790. 'post_status' => 'publish,private,draft,future,pending,inherit,sticky'
  791. ));
  792. $output['meta'] = array();
  793. foreach ($output['posts'] as $k => $p)
  794. $output['meta'][$p->ID] = get_post_meta($p->ID);
  795. foreach ($output['pages'] as $k => $p)
  796. $output['meta'][$p->ID] = get_post_meta($p->ID);
  797. foreach ($output['custom'] as $k => $p)
  798. $output['meta'][$p->ID] = get_post_meta($p->ID);
  799. echo json_encode(cdp_sanitize_array($output));
  800. }
  801. /** –– * */
  802. /** –– **\
  803. * This function will delete all posts in array PERMANENTLY!
  804. * @return object of success message or error
  805. */
  806. function cdp_delete_posts() {
  807. $ids = ((isset($_POST['ids'])) ? cdp_sanitize_array($_POST['ids']) : false); // ids to delete
  808. $throttling = sanitize_text_field($_POST['throttling']); // throttling if enabeld
  809. $thc = sanitize_text_field($_POST['thc']); // throttling count if enabeld
  810. $thrs = sanitize_text_field($_POST['thrs']) == 'true' ? true : false; // trash or not?
  811. $redi = sanitize_text_field($_POST['redi']) == 'true' ? true : false; // redirect if enabled
  812. $auit = sanitize_text_field($_POST['auit']) == 'true' ? true : false; // auit if enabled
  813. $auitd = ((isset($_POST['auitd'])) ? cdp_sanitize_array($_POST['auitd']) : false); // auitd if auit enabled
  814. $prepared_ids = array();
  815. $inGroup = 0;
  816. $curr = current_time('timestamp');
  817. $token = uniqid($curr, true);
  818. $cdp_cron = get_option('_cdp_crons');
  819. $site = is_multisite() ? get_current_blog_id() : '-1';
  820. if ($cdp_cron == false)
  821. $cdp_cron = array();
  822. $cdp_cron[$token] = array(
  823. 'start' => $curr,
  824. 'ids' => $ids,
  825. 'done' => false,
  826. 'shown' => false,
  827. 'f' => 'delete',
  828. 'del_size' => sizeof($ids),
  829. 'handler' => 'cdp_cron_delete',
  830. 'auit' => $auit,
  831. 'auitd' => $auitd
  832. );
  833. $cdp_cron[$token]['tasks'] = array();
  834. $cdp_cron[$token]['args'] = array();
  835. if ($throttling == '1' && $thc && intval($thc) >= 1 && intval($thc) <= 10240) {
  836. $inGroup = ceil(intval($thc) / 30);
  837. for ($i = 0, $k = 2; $i < sizeof($ids); $i = $i + $inGroup, $k++)
  838. $cdp_cron[$token]['tasks']["-$k"] = false;
  839. update_option('_cdp_crons', $cdp_cron);
  840. for ($i = 0, $k = 2; $i < sizeof($ids); $i = $i + $inGroup, $k++) {
  841. $tg = array();
  842. $tt = array('tsk' => "-" . $k, 'token' => $token);
  843. for ($j = $i; $j < ($i + $inGroup); $j++)
  844. if (isset($ids[$j]))
  845. array_push($tg, $ids[$j]);
  846. array_push($prepared_ids, $tg);
  847. $time = $k * 2;
  848. $args = array(array('ids' => $tg, 'site' => $site, 'trash' => $thrs, 'token' => $tt));
  849. wp_schedule_single_event(strtotime("+$time seconds"), 'cdp_cron_delete', $args);
  850. array_push($cdp_cron[$token]['args'], $args);
  851. }
  852. } else {
  853. $cdp_cron[$token]['tasks']["-0"] = false;
  854. update_option('_cdp_crons', $cdp_cron);
  855. $tt = array('tsk' => "-0", 'token' => $token);
  856. $args = array(array('ids' => $ids, 'site' => $site, 'trash' => $thrs, 'token' => $tt));
  857. wp_schedule_single_event(strtotime('+2 seconds'), 'cdp_cron_delete', $args);
  858. array_push($cdp_cron[$token]['args'], $args);
  859. }
  860. echo json_encode(array('status' => 'success', 'token' => cdp_sanitize_array($token)));
  861. }
  862. /** –– * */
  863. /** –– **\
  864. * This function will delete all posts in array PERMANENTLY!
  865. * @return object of success message or error
  866. */
  867. function cdp_clear_all_crons() {
  868. $cdp_cron = get_option('_cdp_crons');
  869. foreach ($cdp_cron as $cron => $val) {
  870. if (array_key_exists('done', $val)) {
  871. if ($val['done'] != true) {
  872. echo json_encode(array(
  873. 'status' => 'fail',
  874. 'type' => 'warning',
  875. 'msg' => __('You can\'t clear messages when tasks are in progress, please firstly kill tasks or wait till the end.', 'copy-delete-posts')
  876. ));
  877. return;
  878. }
  879. }
  880. }
  881. $cdp_cron = delete_option('_cdp_crons');
  882. echo json_encode(array('status' => 'success'));
  883. }
  884. /** –– * */
  885. /** –– **\
  886. * Local function which sets default profile for user
  887. * @return Boolean
  888. */
  889. function cdp_set_default_profile() {
  890. $curr = get_option('_cdp_preselections');
  891. $id = get_current_user_id();
  892. $new = array();
  893. $selection = ((isset($_POST['selection'])) ? cdp_sanitize_array($_POST['selection']) : false);
  894. if ($curr && !is_object($curr) || $curr == false)
  895. $new = array($id => $selection);
  896. else {
  897. $new = $curr;
  898. $new[$id] = $selection;
  899. }
  900. $stat = update_option('_cdp_preselections', $new);
  901. echo cdp_sanitize_array($stat);
  902. }
  903. /** –– * */
  904. /** –– **\
  905. * Local function which gets default profile for user
  906. * @return String
  907. */
  908. function cdp_get_default_profile() {
  909. echo(esc_html(get_option('_cdp_preselections')[get_current_user_id()]));
  910. }
  911. /** –– * */
  912. /** –– **\
  913. * This function will set as seen notification!
  914. * @return object of success message — WARNING: ALWAYS
  915. */
  916. function cdp_set_noti_as_seen() {
  917. if (wp_doing_cron())
  918. return;
  919. $token = ((isset($_POST['noti_token'])) ? sanitize_text_field($_POST['noti_token']) : false);
  920. $cdp_cron = get_option('_cdp_crons', array());
  921. $cdp_cron[$token]['shown'] = true;
  922. update_option('_cdp_crons', $cdp_cron);
  923. echo json_encode(array('status' => 'success'));
  924. }
  925. /** –– * */
  926. /** –– **\
  927. * This function will delete task from the history!
  928. * @return object of success message or fail
  929. */
  930. function cdp_just_hide_task() {
  931. $token = ((isset($_POST['task'])) ? sanitize_text_field($_POST['task']) : false);
  932. $cdp_cron = get_option('_cdp_crons', array());
  933. unset($cdp_cron[$token]);
  934. $res = update_option('_cdp_crons', $cdp_cron);
  935. if ($res)
  936. echo json_encode(array('status' => 'success'));
  937. else
  938. echo json_encode(array('status' => 'fail', 'type' => 'error', 'msg' => __('We can\'t hide this task now, – maybe it\'t already hidden. Please try again later.', 'copy-delete-posts')));
  939. }
  940. /** –– * */
  941. /** –– **\
  942. * This function will kill task from the cron!
  943. * @return object of success message or fail
  944. */
  945. function cdp_just_kill_task() {
  946. $token = ((isset($_POST['task'])) ? sanitize_text_field($_POST['task']) : false);
  947. $cdp_cron = get_option('_cdp_crons', array());
  948. $handler = $cdp_cron[$token]['handler'];
  949. $args = (array_key_exists('args', $cdp_cron[$token]) ? $cdp_cron[$token]['args'] : array());
  950. if ($cdp_cron[$token]['done'] != false) {
  951. echo json_encode(array('status' => 'fail', 'type' => 'error', 'msg' => __('This task has already ended this work, please wait for list refresh and try again.', 'copy-delete-posts')));
  952. return;
  953. }
  954. $status = true;
  955. $res = false;
  956. foreach ($args as $arg => $val) {
  957. $sres = wp_clear_scheduled_hook($handler, $val);
  958. if ($sres == false)
  959. $status = false;
  960. }
  961. if ($cdp_cron[$token]['done'] != false)
  962. $status = true;
  963. if ($status == true) {
  964. unset($cdp_cron[$token]);
  965. $res = update_option('_cdp_crons', $cdp_cron);
  966. }
  967. if ($status || $res)
  968. echo json_encode(array('status' => 'success'));
  969. else
  970. echo json_encode(array('status' => 'fail', 'type' => 'error', 'msg' => __('We can\'t confirm that we killed this task now, please try again later or check if it\'t killed.', 'copy-delete-posts')));
  971. }
  972. /** –– * */
  973. /** –– **\
  974. * This function will catch current cron tasks!
  975. * @return object of tasks or fail
  976. */
  977. function cdp_just_get_tasks() {
  978. $cdp_cron = get_option('_cdp_crons', false);
  979. if ($cdp_cron)
  980. echo json_encode(array('status' => 'success', 'tasks' => cdp_sanitize_array($cdp_cron)));
  981. else
  982. echo json_encode(array('status' => 'fail', 'type' => 'error', 'msg' => __('We couldn\'t catch current tasks, please try again later.', 'copy-delete-posts')));
  983. }
  984. /** –– * */
  985. /** –– **\
  986. * This function will remove performance notice
  987. * @return void
  988. */
  989. function cdp_hide_perf_notice() {
  990. update_option('cdp_dismiss_perf_notice', true);
  991. update_option('cdp_latest_slow_performance', false);
  992. echo json_encode(array('status' => 'success'));
  993. }
  994. /** –– * */
  995. /** –– **\
  996. * This function is just for debug have fun with it!
  997. * It can be fired by function cdp_totally_know_what_i_am_doing('really');
  998. * It won't work in production mode so dont even try it, if you're not me ~ Mikołaj :P
  999. * @return mixed
  1000. */
  1001. function cdp_debug_function() {
  1002. // require_once('C:/Developer/Web/wordpress/wp-content/plugins/copy-delete-posts-premium/classes/methods.php');
  1003. // $settings = get_option('cdpp_aci_settings', false);
  1004. // $meth = new CDP_Premium($settings);
  1005. // $posts = $meth->load_posts($settings['scan']);
  1006. // $filtred = $meth->filter_posts($posts);
  1007. $cdp_cron = get_option('_cdp_crons', false);
  1008. $things_to_debug = array(
  1009. '$cdp_cron' => $cdp_cron
  1010. );
  1011. var_export($things_to_debug);
  1012. }
  1013. /** –– **/