Completed
Pull Request — master (#9)
by
unknown
08:34
created

ShareThisDataObject   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 279
Duplicated Lines 6.45 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 18
loc 279
rs 10
c 0
b 0
f 0
wmc 27
lcom 1
cbo 11

15 Methods

Rating   Name   Duplication   Size   Complexity  
A providePermissions() 0 4 1
A canView() 0 4 1
A canCreate() 0 4 1
A canEdit() 0 4 1
A canDelete() 0 4 1
A IncludeThisIconNice() 0 4 1
A getIncludeThisIconNice() 0 4 2
A IncludeThisIconInExtendedListNice() 0 4 1
A getIncludeThisIconInExtendedListNice() 0 4 2
A Icon() 0 4 1
A getIcon() 0 11 2
A getCMSFields() 0 9 2
A onAfterWrite() 0 5 1
A validate() 0 16 2
B requireDefaultRecords() 18 54 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace SunnysideUp\ShareThis;
4
5
use SilverStripe\Assets\Image;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Forms\LiteralField;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\ORM\DB;
10
use SilverStripe\ORM\FieldType\DBField;
11
use SilverStripe\ORM\ValidationResult;
12
use SilverStripe\Security\Permission;
13
use SilverStripe\Security\PermissionProvider;
14
use SunnysideUp\ShareThis\ShareThisOptions;
15
use SunnysideUp\ShareThis\ShareThisSTE;
16
17
/**
18
 * @author nicolaas[at]sunnysideup.co.nz
19
 * @description: list of Share This Options that can be shown
20
 * @todo finish onAfterWrite and delete objects
21
 */
22
// class ShareThisDataObject extends DataObject implements PermissionProvider
23
class ShareThisDataObject extends DataObject
24
{
25
    /**
26
     * @var string
27
     */
28
    private static $table_name = 'ShareThisDataObject';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
29
30
    /**
31
     * @var array
32
     */
33
    private static $permission_framework = [
34
        "SOCIAL_MEDIA" => [
35
            'name' => "Social Media Management",
36
            'category' => "Social Media",
37
            'help' => 'Edit relationships, links and data of various social media platforms.',
38
            'sort' => 0
39
        ]
40
    ];
41
42
    /**
43
     * @var array
44
     */
45
    private static $db = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
46
        'Title' => 'Varchar(20)',
47
        'IncludeThisIcon' => 'Boolean',
48
        'IncludeThisIconInExtendedList' => 'Boolean',
49
        'Sort' => 'Int'
50
    ];
51
52
    /**
53
     * @var array
54
     */
55
    private static $has_one = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
56
        'AlternativeIcon' => Image::class
57
    ];
58
59
    /**
60
     * @var array
61
     */
62
    private static $casting = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
63
        'Icon' => 'HTMLText',
64
        'IncludeThisIconNice' => 'Varchar',
65
        'IncludeThisIconInExtendedListNice' => 'IncludeThisIconInExtendedList'
66
    ];
67
68
    /**
69
     * @var array
70
     */
71
    private static $field_labels = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
72
        'Title' => 'Name',
73
        'IncludeThisIcon' => 'Include in main list',
74
        'IncludeThisIconNice' => 'Include in primary list',
75
        'IncludeThisIconInExtendedList' => 'Include in secondary list',
76
        'IncludeThisIconInExtendedListNice' => 'Include in secondary list',
77
        'Sort' => 'Sort Index (lower numbers shown first)',
78
        'AlternativeIcon' => 'Optional Alternative Icon (can be any size, a 32px by 32px square is recommended)'
79
    ];
80
81
    /**
82
     * @var array
83
     */
84
    private static $summary_fields = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
85
        'Title' => 'Name',
86
    ];
87
88
    /**
89
     * @var string
90
     */
91
    private static $singular_name = 'Icon to share this page';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
92
93
    /**
94
     * @var string
95
     */
96
    private static $plural_name = 'Icons to share this page';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
97
98
    /**
99
     * @var string
100
     */
101
    private static $default_sort = 'IncludeThisIcon DESC, IncludeThisIconInExtendedList ASC, Sort ASC, Title ASC';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
102
103
    /**
104
     * @return string
105
     */
106
    public function providePermissions()
107
    {
108
        return Config::inst()->get(ShareThisDataObject::class, "permission_framework");
109
    }
110
111
    /**
112
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
113
     */
114
    public function canView($member = null)
115
    {
116
        return Permission::checkMember($member, 'SOCIAL_MEDIA');
117
    }
118
119
    /**
120
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
121
     */
122
    public function canCreate($member = null, $context = [])
123
    {
124
        return Permission::checkMember($member, 'SOCIAL_MEDIA');
125
    }
126
127
    /**
128
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
129
     */
130
    public function canEdit($member = null)
131
    {
132
        return Permission::checkMember($member, 'SOCIAL_MEDIA');
133
    }
134
135
    /**
136
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
137
     */
138
    public function canDelete($member = null)
139
    {
140
        return Permission::checkMember($member, 'SOCIAL_MEDIA');
141
    }
142
143
    /**
144
     * @return string
145
     */
146
    public function IncludeThisIconNice()
147
    {
148
        return $this->getIncludeThisIconNice();
149
    }
150
151
    /**
152
     * @return string
153
     */
154
    public function getIncludeThisIconNice()
155
    {
156
        return $this->IncludeThisIcon ? "Yes" : "No" ;
0 ignored issues
show
Documentation introduced by
The property IncludeThisIcon does not exist on object<SunnysideUp\ShareThis\ShareThisDataObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
157
    }
158
159
    /**
160
     * @return string
161
     */
162
    public function IncludeThisIconInExtendedListNice()
163
    {
164
        return $this->getIncludeThisIconInExtendedListNice();
165
    }
166
167
    /**
168
     * @return string
169
     */
170
    public function getIncludeThisIconInExtendedListNice()
171
    {
172
        return $this->IncludeThisIconInExtendedList ? "Yes" : "No" ;
0 ignored issues
show
Documentation introduced by
The property IncludeThisIconInExtendedList does not exist on object<SunnysideUp\ShareThis\ShareThisDataObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
173
    }
174
175
    /**
176
     * Icon
177
     */
178
    public function Icon()
179
    {
180
        return $this->getIcon();
181
    }
182
183
    /**
184
     * Get the icon
185
     *
186
     * @return  DBField [<description>]
187
     */
188
    public function getIcon()
189
    {
190
        $icon = $this->AlternativeIcon();
0 ignored issues
show
Documentation Bug introduced by
The method AlternativeIcon does not exist on object<SunnysideUp\ShareThis\ShareThisDataObject>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
191
        if ($icon->exists()) {
192
            return $icon->ScaleHeight(16);
193
        }
194
195
        $html = '<img src="' . SS_SHARETHIS_DIR . '/images/icons/' . strtolower($this->Title) . ".png\" alt=\"{$this->Title}\"/>";
196
197
        return DBField::create_field("HTMLText", $html);
198
    }
199
200
    /**
201
     * @return FieldList $fields
0 ignored issues
show
Documentation introduced by
Should the return type not be \SilverStripe\Forms\FieldList?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
202
     */
203
    public function getCMSFields()
204
    {
205
        $fields = parent::getCMSFields();
206
        if (class_exists("DataObjectSorterDOD")) {
207
            $fields->addFieldToTab("Root.Sort", LiteralField::create("SortShortList", $this->dataObjectSorterPopupLink("IncludeThisIcon", 1, "<h3>Sort Main Icons</h3>")));
0 ignored issues
show
Documentation Bug introduced by
The method dataObjectSorterPopupLink does not exist on object<SunnysideUp\ShareThis\ShareThisDataObject>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
208
        }
209
210
        return $fields;
211
    }
212
213
    /**
214
     * @return void
215
     */
216
    public function onAfterWrite()
217
    {
218
        parent::onAfterWrite();
219
        $objects = ShareThisDataObject::get()->filter('Title', $this->Title)->exclude('ID', $this->ID);
0 ignored issues
show
Unused Code introduced by
$objects is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
220
    }
221
222
    /**
223
     * @return ValidationResult $result
224
     */
225
    public function validate()
226
    {
227
        $result = parent::validate();
228
        $bookmarks = ShareThisOptions::get_page_specific_data("", "", "");
229
        if (!isset($bookmarks[$this->Title])) {
230
            $result->addError(sprintf(
231
                _t(
232
                    'ShareThisDataObject.NON_EXISTING_TITLE',
233
                    'This social plaform "%s" does not exist.  Please change / delete the this entry.'
234
                ),
235
                $this->Title
236
            ));
237
        }
238
239
        return $result;
240
    }
241
242
    /**
243
     * Setting default records
244
     *
245
     * @return void
246
     */
247
    public function requireDefaultRecords()
248
    {
249
        parent::requireDefaultRecords();
250
        $actualArray = ShareThisOptions::get_general_data();
251
        Config::inst()->update(ShareThisSTE::class, "included_icons", []);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...s\DeltaConfigCollection, SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
252
        Config::inst()->update(ShareThisSTE::class, "excluded_icons", []);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...s\DeltaConfigCollection, SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
253
        ShareThisOptions::set_general_data(null);
0 ignored issues
show
Unused Code introduced by
The call to ShareThisOptions::set_general_data() has too many arguments starting with null.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
254
        $fullArray = ShareThisOptions::get_general_data();
255
256
        foreach ($fullArray as $key) {
257
            $object = ShareThisDataObject::get()->filter('Title', $key);
258
259
            if (!$object->exists()) {
260
                $object = new ShareThisDataObject();
261
                $object->Title = $key;
262
                $style = 'excluded';
263
                $object->IncludeThisIcon = false;
0 ignored issues
show
Documentation introduced by
The property IncludeThisIcon does not exist on object<SunnysideUp\ShareThis\ShareThisDataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
264
265
                if (in_array($key, $actualArray)) {
266
                    $object->IncludeThisIcon = true;
0 ignored issues
show
Documentation introduced by
The property IncludeThisIcon does not exist on object<SunnysideUp\ShareThis\ShareThisDataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
267
                    $style = 'included';
268
                }
269
270
                $object->write();
271
                DB::alteration_message("Added Bookmark Icon for $key ($style)", 'created');
272
            }
273
        }
274
275
        $inc = Config::inst()->get(ShareThisSTE::class, "included_icons");
276
277 View Code Duplication
        foreach ($inc as $key) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
278
            $object = ShareThisDataObject::get()->filter(['Title' => $key, 'IncludeThisIcon' => 0]);
279
280
            if ($object->exists()) {
281
                $object = $object->first();
282
                $object->IncludeThisIcon = true;
283
                $object->write();
284
                DB::alteration_message("Updated inclusion for $key", 'created');
285
            }
286
        }
287
288
        $exc = Config::inst()->get(ShareThisSTE::class, "excluded_icons");
289
290 View Code Duplication
        foreach ($exc as $key) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
            $object = ShareThisDataObject::get()->filter(['Title' => $key, 'IncludeThisIcon' => 1]);
292
293
            if ($object->exists()) {
294
                $object = $object->first();
295
                $object->IncludeThisIcon = false;
296
                $object->write();
297
                DB::alteration_message("Updated inclusion for $key", 'created');
298
            }
299
        }
300
    }
301
}
302